plures 1.15.0 → 1.16.0
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.md +3 -1
- package/dist/{chunk-JSR4MDEK.js → chunk-Z75ZTXHZ.js} +31 -2
- package/dist/cli.cjs +120 -3
- package/dist/cli.js +90 -2
- package/dist/index.cjs +31 -2
- package/dist/index.d.cts +96 -3
- package/dist/index.d.ts +96 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,10 +44,12 @@ if (event.type === "task.completed" && event.data.result?.agreement === 1) {
|
|
|
44
44
|
|
|
45
45
|
## API
|
|
46
46
|
|
|
47
|
+
- `plures.check(id, data, overrides?)`: run a check from your app's map, one line before the action it guards: `const check = await plures.check("refunds", { order_total, reason }); if (check.answer !== "Approve") return`. The check saved on Plures supplies the question, options, which fields people see (the rest come back in `kept_private`), who decides, how often, its conditions and what happens on a timeout, so you change them on Plures, not in code. `checked` says whether a person looked; `skipped_because` says why not. Overrides: `question`, `options`, `verifiers`, `team`, `route`, `sample`, `timeout`, `always`.
|
|
48
|
+
- `npx plures check add <id> --app <app>`: Claude Code puts that line before the action on a new branch. On GitHub (the dashboard's **Add it to my code**) Plures then opens a pull request.
|
|
47
49
|
- `plures.tasks.create(params, { idempotencyKey? })`: send a task. Params: `question`, `options` (2 to 8, strings or `{ id, label }`), `context`, `image_url`, `audio_url` or `video_url` with `media_seconds` (a clip up to 180 seconds that people play through before answering), `sensitive`, `instructions`, `verifiers` (1, 3, 5, 7 or 9), `max_verifiers`, `presence_check` (default true), `webhook_url`, `metadata`, `expires_in`, `route` (`"team"` sends it straight to your own people, $0.05 per internal vote live), `category` (matches your routing rules) and `team` (one of your named teams), `team_votes` and `team_unanimous` (internal consensus: how many of your team vote, and whether they must all agree; a split goes to the team's lead).
|
|
48
50
|
- `plures.tasks.retrieve(id)`, `plures.tasks.list({ status?, limit?, starting_after?, app? })`, `plures.tasks.cancel(id)`
|
|
49
51
|
- `new Plures(key, { app: "support-agent" })` or `PLURES_APP`: send everything to one of your apps. `plures.apps.list()`, `.create({ name, id? })`, `.retrieve(id)`, `.update(id, { name?, id?, default? })`, `.del(id)`. Each app keeps its own tasks, ledger view, map and quality.
|
|
50
|
-
- `plures.maps.guide()`, `.retrieve(app)`, `.upload(app, map)
|
|
52
|
+
- `plures.maps.guide()`, `.retrieve(app)`, `.upload(app, map, { branch? })` (a branch other than main gets its own map, compared with main and noted on its pull request), `.setCheckpoint(app, id, status)`: the agent map, where your agent needs a person. Or run `npx plures map` in your repo: your own coding agent reads the code and only the map is sent.
|
|
51
53
|
- `plures.maps.createKey(app, { label? })`, `.keys(app)`, `.revokeKey(app, id)`: map keys for CI, which can only read and upload that app's map. `.workflow(app, branch?)` returns the GitHub Action that maps on every push; `npx plures map workflow` writes it to `.github/workflows/plures-map.yml`. Or use **Map with GitHub** on the app's map page and Plures opens the pull request for you, then **Turn on mapping** merges it. `plures.maps.startRun(app)` and `.runs(app)`: in CI with no Claude key, `npx plures map` starts a run where Plures runs Claude (first map free, then Claude's cost plus 30%, at most $5).
|
|
52
54
|
- In your terminal, `npx plures map` opens your map page so you can watch it build (`--no-open` to skip), keeps one live line of what it's reading, and ends by drawing the map's flows with the checks Plures suggests. On GitHub and other CI it prints a plain log.
|
|
53
55
|
- `npx plures map` shows each file as Claude Code reads it and sends the file names (never their contents) to Plures, so the map page shows the reading live and the map builds itself when it lands. `plures.maps.progress(app, { session, phase, events })` is the call behind it.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
var VERSION = "1.
|
|
2
|
+
var VERSION = "1.16.0";
|
|
3
3
|
var PluresError = class extends Error {
|
|
4
4
|
constructor(message, status, type) {
|
|
5
5
|
super(message);
|
|
@@ -100,7 +100,8 @@ var Plures = class {
|
|
|
100
100
|
},
|
|
101
101
|
retrieve: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map`),
|
|
102
102
|
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
103
|
-
|
|
103
|
+
/** A branch other than the main one gets its own map, compared with main, and noted on its pull request. */
|
|
104
|
+
upload: (app, map, opts = {}) => this.req("PUT", `/api/v1/apps/${encodeURIComponent(app)}/map`, map, opts.branch ? { "plures-branch": opts.branch } : {}),
|
|
104
105
|
setCheckpoint: (app, id, status) => this.req("PATCH", `/api/v1/apps/${encodeURIComponent(app)}/map/checkpoints/${encodeURIComponent(id)}`, { status }),
|
|
105
106
|
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
106
107
|
createKey: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`, opts),
|
|
@@ -111,6 +112,8 @@ var Plures = class {
|
|
|
111
112
|
runs: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`),
|
|
112
113
|
/** While mapping, which files the coding agent is reading, shown live on the map page. File names only. npx plures map does this. */
|
|
113
114
|
progress: (app, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map/progress`, p),
|
|
115
|
+
/** After npx plures check add pushed a branch with the check in it: Plures opens the pull request. Pass error instead if adding it failed. */
|
|
116
|
+
checkPr: (app, id, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/checks/${encodeURIComponent(id)}/pr`, p),
|
|
114
117
|
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
115
118
|
workflow: async (app, branch = "main") => {
|
|
116
119
|
const r = await fetch(`${this.base}/api/v1/map_workflow?app=${encodeURIComponent(app)}&branch=${encodeURIComponent(branch)}`);
|
|
@@ -168,6 +171,32 @@ var Plures = class {
|
|
|
168
171
|
};
|
|
169
172
|
/** Your prepaid credit. A live task that could cost more than available_cents is refused with a 402 insufficient_credits error. */
|
|
170
173
|
balance = () => this.req("GET", "/api/v1/balance");
|
|
174
|
+
/**
|
|
175
|
+
* Runs a check from your app's map and waits for the decision. Send the data by name; the check
|
|
176
|
+
* saved on Plures supplies the question, options, guidance, which fields people see (the rest
|
|
177
|
+
* never leave Plures), who decides, how often and what happens on a timeout. Any of those can be
|
|
178
|
+
* overridden for this call. `answer` is the option to act on: what people chose, the first option
|
|
179
|
+
* when the check wasn't needed (a sample or a condition) or timed out set to go ahead, and null
|
|
180
|
+
* when it timed out set to hold.
|
|
181
|
+
*/
|
|
182
|
+
async check(id, data = {}, overrides = {}, opts = {}) {
|
|
183
|
+
const t = await this.req("POST", `/api/v1/checks/${encodeURIComponent(id)}`, { data, ...overrides }, opts.idempotencyKey ? { "idempotency-key": opts.idempotencyKey } : {});
|
|
184
|
+
const final = t.status === "pending" ? await this.tasks.waitFor(t.id, { timeoutMs: opts.timeoutMs }) : t;
|
|
185
|
+
const opt = (k) => final.options.find((o) => o.id === k)?.label ?? k ?? null;
|
|
186
|
+
const first = final.options[0]?.label ?? null;
|
|
187
|
+
const timedOut = final.status === "expired" || final.status === "pending";
|
|
188
|
+
const answer = final.status === "completed" ? opt(final.result?.answer) : final.status === "skipped" ? first : timedOut && t.on_timeout === "proceed" ? first : null;
|
|
189
|
+
return {
|
|
190
|
+
...final,
|
|
191
|
+
check: t.check,
|
|
192
|
+
on_timeout: t.on_timeout,
|
|
193
|
+
kept_private: t.kept_private ?? [],
|
|
194
|
+
skipped_because: t.skipped_because ?? null,
|
|
195
|
+
answer,
|
|
196
|
+
checked: final.status !== "skipped",
|
|
197
|
+
timed_out: timedOut
|
|
198
|
+
};
|
|
199
|
+
}
|
|
171
200
|
/** Creates a task and waits for people to answer it. */
|
|
172
201
|
async verify(params, opts = {}) {
|
|
173
202
|
const t = await this.tasks.create(params, { idempotencyKey: opts.idempotencyKey });
|
package/dist/cli.cjs
CHANGED
|
@@ -30,7 +30,7 @@ var import_node_path = require("path");
|
|
|
30
30
|
var import_node_fs = require("fs");
|
|
31
31
|
|
|
32
32
|
// src/index.ts
|
|
33
|
-
var VERSION = "1.
|
|
33
|
+
var VERSION = "1.16.0";
|
|
34
34
|
var PluresError = class extends Error {
|
|
35
35
|
constructor(message, status, type) {
|
|
36
36
|
super(message);
|
|
@@ -131,7 +131,8 @@ var Plures = class {
|
|
|
131
131
|
},
|
|
132
132
|
retrieve: (app2) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app2)}/map`),
|
|
133
133
|
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
134
|
-
|
|
134
|
+
/** A branch other than the main one gets its own map, compared with main, and noted on its pull request. */
|
|
135
|
+
upload: (app2, map2, opts = {}) => this.req("PUT", `/api/v1/apps/${encodeURIComponent(app2)}/map`, map2, opts.branch ? { "plures-branch": opts.branch } : {}),
|
|
135
136
|
setCheckpoint: (app2, id, status) => this.req("PATCH", `/api/v1/apps/${encodeURIComponent(app2)}/map/checkpoints/${encodeURIComponent(id)}`, { status }),
|
|
136
137
|
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
137
138
|
createKey: (app2, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/map_keys`, opts),
|
|
@@ -142,6 +143,8 @@ var Plures = class {
|
|
|
142
143
|
runs: (app2) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app2)}/map_runs`),
|
|
143
144
|
/** While mapping, which files the coding agent is reading, shown live on the map page. File names only. npx plures map does this. */
|
|
144
145
|
progress: (app2, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/map/progress`, p),
|
|
146
|
+
/** After npx plures check add pushed a branch with the check in it: Plures opens the pull request. Pass error instead if adding it failed. */
|
|
147
|
+
checkPr: (app2, id, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app2)}/checks/${encodeURIComponent(id)}/pr`, p),
|
|
145
148
|
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
146
149
|
workflow: async (app2, branch2 = "main") => {
|
|
147
150
|
const r = await fetch(`${this.base}/api/v1/map_workflow?app=${encodeURIComponent(app2)}&branch=${encodeURIComponent(branch2)}`);
|
|
@@ -199,6 +202,32 @@ var Plures = class {
|
|
|
199
202
|
};
|
|
200
203
|
/** Your prepaid credit. A live task that could cost more than available_cents is refused with a 402 insufficient_credits error. */
|
|
201
204
|
balance = () => this.req("GET", "/api/v1/balance");
|
|
205
|
+
/**
|
|
206
|
+
* Runs a check from your app's map and waits for the decision. Send the data by name; the check
|
|
207
|
+
* saved on Plures supplies the question, options, guidance, which fields people see (the rest
|
|
208
|
+
* never leave Plures), who decides, how often and what happens on a timeout. Any of those can be
|
|
209
|
+
* overridden for this call. `answer` is the option to act on: what people chose, the first option
|
|
210
|
+
* when the check wasn't needed (a sample or a condition) or timed out set to go ahead, and null
|
|
211
|
+
* when it timed out set to hold.
|
|
212
|
+
*/
|
|
213
|
+
async check(id, data = {}, overrides = {}, opts = {}) {
|
|
214
|
+
const t = await this.req("POST", `/api/v1/checks/${encodeURIComponent(id)}`, { data, ...overrides }, opts.idempotencyKey ? { "idempotency-key": opts.idempotencyKey } : {});
|
|
215
|
+
const final = t.status === "pending" ? await this.tasks.waitFor(t.id, { timeoutMs: opts.timeoutMs }) : t;
|
|
216
|
+
const opt = (k) => final.options.find((o) => o.id === k)?.label ?? k ?? null;
|
|
217
|
+
const first = final.options[0]?.label ?? null;
|
|
218
|
+
const timedOut = final.status === "expired" || final.status === "pending";
|
|
219
|
+
const answer = final.status === "completed" ? opt(final.result?.answer) : final.status === "skipped" ? first : timedOut && t.on_timeout === "proceed" ? first : null;
|
|
220
|
+
return {
|
|
221
|
+
...final,
|
|
222
|
+
check: t.check,
|
|
223
|
+
on_timeout: t.on_timeout,
|
|
224
|
+
kept_private: t.kept_private ?? [],
|
|
225
|
+
skipped_because: t.skipped_because ?? null,
|
|
226
|
+
answer,
|
|
227
|
+
checked: final.status !== "skipped",
|
|
228
|
+
timed_out: timedOut
|
|
229
|
+
};
|
|
230
|
+
}
|
|
202
231
|
/** Creates a task and waits for people to answer it. */
|
|
203
232
|
async verify(params, opts = {}) {
|
|
204
233
|
const t = await this.tasks.create(params, { idempotencyKey: opts.idempotencyKey });
|
|
@@ -446,11 +475,19 @@ async function upload(file) {
|
|
|
446
475
|
}
|
|
447
476
|
if (!fancy) console.log(dim(`Sending the map to Plures. No source code is sent.`));
|
|
448
477
|
let m;
|
|
478
|
+
const gitBranch = (0, import_node_child_process.spawnSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" }).stdout?.trim();
|
|
479
|
+
const branchName = process.env.PLURES_BRANCH || process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || (gitBranch && gitBranch !== "HEAD" ? gitBranch : "");
|
|
449
480
|
try {
|
|
450
|
-
m = await client().maps.upload(app, map2);
|
|
481
|
+
m = await client().maps.upload(app, map2, branchName ? { branch: branchName } : {});
|
|
451
482
|
} catch (e) {
|
|
452
483
|
die(e instanceof PluresError ? `Plures said: ${e.message}` : String(e));
|
|
453
484
|
}
|
|
485
|
+
if (m.branch) console.log(`${bold(`Branch ${m.branch}`)}: compared with the main map${m.main_version ? ` (version ${m.main_version})` : ""}, not replacing it.${m.pull_request ? ` Noted on ${m.pull_request}` : ""}`);
|
|
486
|
+
if (m.changes && (m.changes.added.length || m.changes.removed.length || m.changes.renamed.length || m.changes.risky.length)) {
|
|
487
|
+
const c2 = m.changes;
|
|
488
|
+
console.log(`${dim("Changes:")} ${[c2.added.length ? `${c2.added.length} new` : "", c2.renamed.length ? `${c2.renamed.length} renamed, checks kept` : "", c2.removed.length ? `${c2.removed.length} removed` : ""].filter(Boolean).join(", ") || "no new actions"}`);
|
|
489
|
+
for (const r of c2.risky) console.log(` ${coral("no check")} ${r.label} ${dim(`score ${r.score}: ${r.why.join("; ")}`)}`);
|
|
490
|
+
}
|
|
454
491
|
if (fancy) drawMap(m, m.url ?? `${base}/dashboard/apps/${m.app}/map`);
|
|
455
492
|
else report(m);
|
|
456
493
|
}
|
|
@@ -669,8 +706,87 @@ It adds one workflow file. Review it, then turn mapping on with ${cyan("npx plur
|
|
|
669
706
|
throw e;
|
|
670
707
|
}
|
|
671
708
|
}
|
|
709
|
+
async function checkAdd(id) {
|
|
710
|
+
if (!id) die("npx plures check add <check id> [--app <app>]");
|
|
711
|
+
const cl = client();
|
|
712
|
+
const report2 = async (p) => {
|
|
713
|
+
if (process.env.GITHUB_ACTIONS) await cl.maps.checkPr(app, id, p).catch(() => {
|
|
714
|
+
});
|
|
715
|
+
};
|
|
716
|
+
const fail = async (msg) => {
|
|
717
|
+
await report2({ error: msg });
|
|
718
|
+
die(msg);
|
|
719
|
+
};
|
|
720
|
+
let m;
|
|
721
|
+
try {
|
|
722
|
+
m = await cl.maps.retrieve(app);
|
|
723
|
+
} catch (e) {
|
|
724
|
+
return fail(e instanceof PluresError ? e.message : String(e));
|
|
725
|
+
}
|
|
726
|
+
const cp0 = m.checkpoints.find((x) => x.id === id || x.category === id);
|
|
727
|
+
const cp = cp0;
|
|
728
|
+
if (!cp0) return fail(`No check called ${id} on ${app}'s map.`);
|
|
729
|
+
const action = m.nodes.find((n) => n.id === cp.at);
|
|
730
|
+
const cid = cp.id ?? id;
|
|
731
|
+
if (!cp.code) return fail(`The check ${cid} has no drafted question yet. Edit it on Plures first.`);
|
|
732
|
+
if (!hasClaude()) return fail("Claude Code isn't installed here, so nothing could add the line. Copy it from the check's page instead.");
|
|
733
|
+
const git = (...a) => (0, import_node_child_process.spawnSync)("git", a, { encoding: "utf8" });
|
|
734
|
+
if (git("status", "--porcelain").stdout.trim()) return fail("This repo has changes that aren't committed. Commit or stash them first.");
|
|
735
|
+
const childEnv = { ...process.env };
|
|
736
|
+
const ownClaude = Boolean(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_AUTH_TOKEN);
|
|
737
|
+
if (!ownClaude && process.env.CI) {
|
|
738
|
+
try {
|
|
739
|
+
const run = await cl.maps.startRun(app);
|
|
740
|
+
childEnv.ANTHROPIC_BASE_URL = run.base_url;
|
|
741
|
+
childEnv.ANTHROPIC_API_KEY = run.token;
|
|
742
|
+
} catch (e) {
|
|
743
|
+
return fail(e instanceof PluresError ? e.message : String(e));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
const where = action?.file ? `${action.file}${action.line ? ` near line ${action.line}` : ""}` : "the code";
|
|
747
|
+
console.log(`Adding the check ${bold(cid)} before ${bold(action?.label ?? cp.at)}, in ${where}.`);
|
|
748
|
+
const prompt = `Add one Plures human check to this codebase. Change as little as possible.
|
|
749
|
+
|
|
750
|
+
Where: just before the step "${action?.label ?? cp.at}"${action?.via ? ` (it goes through ${action.via})` : ""}, in ${where}. Find the exact place; the line may have moved.
|
|
751
|
+
|
|
752
|
+
What to add, adapted to the file's language and style (for Python: \`check = plures.check("${cid}", fields)\` and \`check.answer\`):
|
|
753
|
+
|
|
754
|
+
${cp.code}
|
|
755
|
+
|
|
756
|
+
- fields: an object with the values a person needs to decide, taken from variables already in scope at that point${cp.draft?.show?.length ? `. Use these field names: ${cp.draft.show.join(", ")}` : ""}. Never pass secrets, card numbers or passwords.
|
|
757
|
+
- If the answer isn't the first option, the agent must not do the step: return, skip, or raise, whichever fits the code around it.
|
|
758
|
+
- If the file has no Plures client yet, add one: JavaScript or TypeScript \`import { Plures } from "plures"\` and \`const plures = new Plures(process.env.PLURES_API_KEY${app !== "default" ? `, { app: "${app}" }` : ""})\`; Python \`from plures import Plures\` and \`plures = Plures(os.environ["PLURES_API_KEY"]${app !== "default" ? `, app="${app}"` : ""})\`. Keep to how the project sets up other clients.
|
|
759
|
+
- If the package isn't a dependency yet, add "plures" to package.json dependencies, or plures to requirements.txt or pyproject.toml, whichever the project uses. Don't run installers.
|
|
760
|
+
- Don't change anything else, don't reformat, don't add tests, and don't run commands.`;
|
|
761
|
+
const code = await new Promise((resolve) => {
|
|
762
|
+
const p = (0, import_node_child_process.spawn)("claude", ["-p", prompt, "--allowedTools", "Read,Grep,Glob,Edit,Write"], { stdio: ["ignore", "inherit", "inherit"], env: childEnv });
|
|
763
|
+
p.on("close", (n) => resolve(n ?? 1));
|
|
764
|
+
p.on("error", () => resolve(1));
|
|
765
|
+
});
|
|
766
|
+
const changed = git("status", "--porcelain").stdout.trim();
|
|
767
|
+
if (code !== 0 || !changed) return fail("Claude Code didn't change any files, so there's nothing to review. Copy the line from the check's page instead.");
|
|
768
|
+
const br = `plures/check-${cid.replace(/[^a-z0-9_-]+/gi, "-")}-${Date.now().toString(36)}`;
|
|
769
|
+
for (const a of [["checkout", "-b", br], ["add", "-A"], ["commit", "-m", `Add the Plures check ${cid} before ${(action?.label ?? cp.at).toLowerCase()}`]]) {
|
|
770
|
+
const r = git(...a);
|
|
771
|
+
if (r.status !== 0) return fail(`git ${a[0]} failed: ${(r.stderr || r.stdout).trim().slice(0, 200)}`);
|
|
772
|
+
}
|
|
773
|
+
if (!process.env.GITHUB_ACTIONS) {
|
|
774
|
+
console.log(`
|
|
775
|
+
${cyan("\u2713")} The check is on the branch ${bold(br)}. Review it with ${cyan("git show")}, then push it and open a pull request.`);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const push = git("push", "-u", "origin", br);
|
|
779
|
+
if (push.status !== 0) return fail(`Pushing the branch failed: ${(push.stderr || push.stdout).trim().slice(0, 200)}`);
|
|
780
|
+
try {
|
|
781
|
+
const r = await cl.maps.checkPr(app, cid, { branch: br });
|
|
782
|
+
console.log(`${cyan("\u2713")} Pull request: ${r.pull_request ?? "opened"}`);
|
|
783
|
+
} catch (e) {
|
|
784
|
+
die(e instanceof PluresError ? e.message : String(e));
|
|
785
|
+
}
|
|
786
|
+
}
|
|
672
787
|
async function main() {
|
|
673
788
|
const [cmd, sub, arg] = argv;
|
|
789
|
+
if (cmd === "check" && sub === "add") return checkAdd(arg);
|
|
674
790
|
if (cmd !== "map") {
|
|
675
791
|
console.log(`Plures
|
|
676
792
|
|
|
@@ -681,6 +797,7 @@ async function main() {
|
|
|
681
797
|
npx plures map github connect let Plures add that Action by pull request
|
|
682
798
|
npx plures map github on merge it, which turns mapping on
|
|
683
799
|
npx plures map github run map now on GitHub, and watch it build
|
|
800
|
+
npx plures check add <id> put a check into your code, on a new branch
|
|
684
801
|
|
|
685
802
|
Docs: https://plures.si/docs`);
|
|
686
803
|
return;
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
Plures,
|
|
4
4
|
PluresError,
|
|
5
5
|
VERSION
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z75ZTXHZ.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
import { spawnSync, spawn } from "child_process";
|
|
@@ -228,11 +228,19 @@ async function upload(file) {
|
|
|
228
228
|
}
|
|
229
229
|
if (!fancy) console.log(dim(`Sending the map to Plures. No source code is sent.`));
|
|
230
230
|
let m;
|
|
231
|
+
const gitBranch = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf8" }).stdout?.trim();
|
|
232
|
+
const branchName = process.env.PLURES_BRANCH || process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || (gitBranch && gitBranch !== "HEAD" ? gitBranch : "");
|
|
231
233
|
try {
|
|
232
|
-
m = await client().maps.upload(app, map2);
|
|
234
|
+
m = await client().maps.upload(app, map2, branchName ? { branch: branchName } : {});
|
|
233
235
|
} catch (e) {
|
|
234
236
|
die(e instanceof PluresError ? `Plures said: ${e.message}` : String(e));
|
|
235
237
|
}
|
|
238
|
+
if (m.branch) console.log(`${bold(`Branch ${m.branch}`)}: compared with the main map${m.main_version ? ` (version ${m.main_version})` : ""}, not replacing it.${m.pull_request ? ` Noted on ${m.pull_request}` : ""}`);
|
|
239
|
+
if (m.changes && (m.changes.added.length || m.changes.removed.length || m.changes.renamed.length || m.changes.risky.length)) {
|
|
240
|
+
const c2 = m.changes;
|
|
241
|
+
console.log(`${dim("Changes:")} ${[c2.added.length ? `${c2.added.length} new` : "", c2.renamed.length ? `${c2.renamed.length} renamed, checks kept` : "", c2.removed.length ? `${c2.removed.length} removed` : ""].filter(Boolean).join(", ") || "no new actions"}`);
|
|
242
|
+
for (const r of c2.risky) console.log(` ${coral("no check")} ${r.label} ${dim(`score ${r.score}: ${r.why.join("; ")}`)}`);
|
|
243
|
+
}
|
|
236
244
|
if (fancy) drawMap(m, m.url ?? `${base}/dashboard/apps/${m.app}/map`);
|
|
237
245
|
else report(m);
|
|
238
246
|
}
|
|
@@ -451,8 +459,87 @@ It adds one workflow file. Review it, then turn mapping on with ${cyan("npx plur
|
|
|
451
459
|
throw e;
|
|
452
460
|
}
|
|
453
461
|
}
|
|
462
|
+
async function checkAdd(id) {
|
|
463
|
+
if (!id) die("npx plures check add <check id> [--app <app>]");
|
|
464
|
+
const cl = client();
|
|
465
|
+
const report2 = async (p) => {
|
|
466
|
+
if (process.env.GITHUB_ACTIONS) await cl.maps.checkPr(app, id, p).catch(() => {
|
|
467
|
+
});
|
|
468
|
+
};
|
|
469
|
+
const fail = async (msg) => {
|
|
470
|
+
await report2({ error: msg });
|
|
471
|
+
die(msg);
|
|
472
|
+
};
|
|
473
|
+
let m;
|
|
474
|
+
try {
|
|
475
|
+
m = await cl.maps.retrieve(app);
|
|
476
|
+
} catch (e) {
|
|
477
|
+
return fail(e instanceof PluresError ? e.message : String(e));
|
|
478
|
+
}
|
|
479
|
+
const cp0 = m.checkpoints.find((x) => x.id === id || x.category === id);
|
|
480
|
+
const cp = cp0;
|
|
481
|
+
if (!cp0) return fail(`No check called ${id} on ${app}'s map.`);
|
|
482
|
+
const action = m.nodes.find((n) => n.id === cp.at);
|
|
483
|
+
const cid = cp.id ?? id;
|
|
484
|
+
if (!cp.code) return fail(`The check ${cid} has no drafted question yet. Edit it on Plures first.`);
|
|
485
|
+
if (!hasClaude()) return fail("Claude Code isn't installed here, so nothing could add the line. Copy it from the check's page instead.");
|
|
486
|
+
const git = (...a) => spawnSync("git", a, { encoding: "utf8" });
|
|
487
|
+
if (git("status", "--porcelain").stdout.trim()) return fail("This repo has changes that aren't committed. Commit or stash them first.");
|
|
488
|
+
const childEnv = { ...process.env };
|
|
489
|
+
const ownClaude = Boolean(process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_AUTH_TOKEN);
|
|
490
|
+
if (!ownClaude && process.env.CI) {
|
|
491
|
+
try {
|
|
492
|
+
const run = await cl.maps.startRun(app);
|
|
493
|
+
childEnv.ANTHROPIC_BASE_URL = run.base_url;
|
|
494
|
+
childEnv.ANTHROPIC_API_KEY = run.token;
|
|
495
|
+
} catch (e) {
|
|
496
|
+
return fail(e instanceof PluresError ? e.message : String(e));
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const where = action?.file ? `${action.file}${action.line ? ` near line ${action.line}` : ""}` : "the code";
|
|
500
|
+
console.log(`Adding the check ${bold(cid)} before ${bold(action?.label ?? cp.at)}, in ${where}.`);
|
|
501
|
+
const prompt = `Add one Plures human check to this codebase. Change as little as possible.
|
|
502
|
+
|
|
503
|
+
Where: just before the step "${action?.label ?? cp.at}"${action?.via ? ` (it goes through ${action.via})` : ""}, in ${where}. Find the exact place; the line may have moved.
|
|
504
|
+
|
|
505
|
+
What to add, adapted to the file's language and style (for Python: \`check = plures.check("${cid}", fields)\` and \`check.answer\`):
|
|
506
|
+
|
|
507
|
+
${cp.code}
|
|
508
|
+
|
|
509
|
+
- fields: an object with the values a person needs to decide, taken from variables already in scope at that point${cp.draft?.show?.length ? `. Use these field names: ${cp.draft.show.join(", ")}` : ""}. Never pass secrets, card numbers or passwords.
|
|
510
|
+
- If the answer isn't the first option, the agent must not do the step: return, skip, or raise, whichever fits the code around it.
|
|
511
|
+
- If the file has no Plures client yet, add one: JavaScript or TypeScript \`import { Plures } from "plures"\` and \`const plures = new Plures(process.env.PLURES_API_KEY${app !== "default" ? `, { app: "${app}" }` : ""})\`; Python \`from plures import Plures\` and \`plures = Plures(os.environ["PLURES_API_KEY"]${app !== "default" ? `, app="${app}"` : ""})\`. Keep to how the project sets up other clients.
|
|
512
|
+
- If the package isn't a dependency yet, add "plures" to package.json dependencies, or plures to requirements.txt or pyproject.toml, whichever the project uses. Don't run installers.
|
|
513
|
+
- Don't change anything else, don't reformat, don't add tests, and don't run commands.`;
|
|
514
|
+
const code = await new Promise((resolve) => {
|
|
515
|
+
const p = spawn("claude", ["-p", prompt, "--allowedTools", "Read,Grep,Glob,Edit,Write"], { stdio: ["ignore", "inherit", "inherit"], env: childEnv });
|
|
516
|
+
p.on("close", (n) => resolve(n ?? 1));
|
|
517
|
+
p.on("error", () => resolve(1));
|
|
518
|
+
});
|
|
519
|
+
const changed = git("status", "--porcelain").stdout.trim();
|
|
520
|
+
if (code !== 0 || !changed) return fail("Claude Code didn't change any files, so there's nothing to review. Copy the line from the check's page instead.");
|
|
521
|
+
const br = `plures/check-${cid.replace(/[^a-z0-9_-]+/gi, "-")}-${Date.now().toString(36)}`;
|
|
522
|
+
for (const a of [["checkout", "-b", br], ["add", "-A"], ["commit", "-m", `Add the Plures check ${cid} before ${(action?.label ?? cp.at).toLowerCase()}`]]) {
|
|
523
|
+
const r = git(...a);
|
|
524
|
+
if (r.status !== 0) return fail(`git ${a[0]} failed: ${(r.stderr || r.stdout).trim().slice(0, 200)}`);
|
|
525
|
+
}
|
|
526
|
+
if (!process.env.GITHUB_ACTIONS) {
|
|
527
|
+
console.log(`
|
|
528
|
+
${cyan("\u2713")} The check is on the branch ${bold(br)}. Review it with ${cyan("git show")}, then push it and open a pull request.`);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const push = git("push", "-u", "origin", br);
|
|
532
|
+
if (push.status !== 0) return fail(`Pushing the branch failed: ${(push.stderr || push.stdout).trim().slice(0, 200)}`);
|
|
533
|
+
try {
|
|
534
|
+
const r = await cl.maps.checkPr(app, cid, { branch: br });
|
|
535
|
+
console.log(`${cyan("\u2713")} Pull request: ${r.pull_request ?? "opened"}`);
|
|
536
|
+
} catch (e) {
|
|
537
|
+
die(e instanceof PluresError ? e.message : String(e));
|
|
538
|
+
}
|
|
539
|
+
}
|
|
454
540
|
async function main() {
|
|
455
541
|
const [cmd, sub, arg] = argv;
|
|
542
|
+
if (cmd === "check" && sub === "add") return checkAdd(arg);
|
|
456
543
|
if (cmd !== "map") {
|
|
457
544
|
console.log(`Plures
|
|
458
545
|
|
|
@@ -463,6 +550,7 @@ async function main() {
|
|
|
463
550
|
npx plures map github connect let Plures add that Action by pull request
|
|
464
551
|
npx plures map github on merge it, which turns mapping on
|
|
465
552
|
npx plures map github run map now on GitHub, and watch it build
|
|
553
|
+
npx plures check add <id> put a check into your code, on a new branch
|
|
466
554
|
|
|
467
555
|
Docs: https://plures.si/docs`);
|
|
468
556
|
return;
|
package/dist/index.cjs
CHANGED
|
@@ -26,7 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
default: () => index_default
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(index_exports);
|
|
29
|
-
var VERSION = "1.
|
|
29
|
+
var VERSION = "1.16.0";
|
|
30
30
|
var PluresError = class extends Error {
|
|
31
31
|
constructor(message, status, type) {
|
|
32
32
|
super(message);
|
|
@@ -127,7 +127,8 @@ var Plures = class {
|
|
|
127
127
|
},
|
|
128
128
|
retrieve: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map`),
|
|
129
129
|
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
130
|
-
|
|
130
|
+
/** A branch other than the main one gets its own map, compared with main, and noted on its pull request. */
|
|
131
|
+
upload: (app, map, opts = {}) => this.req("PUT", `/api/v1/apps/${encodeURIComponent(app)}/map`, map, opts.branch ? { "plures-branch": opts.branch } : {}),
|
|
131
132
|
setCheckpoint: (app, id, status) => this.req("PATCH", `/api/v1/apps/${encodeURIComponent(app)}/map/checkpoints/${encodeURIComponent(id)}`, { status }),
|
|
132
133
|
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
133
134
|
createKey: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`, opts),
|
|
@@ -138,6 +139,8 @@ var Plures = class {
|
|
|
138
139
|
runs: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`),
|
|
139
140
|
/** While mapping, which files the coding agent is reading, shown live on the map page. File names only. npx plures map does this. */
|
|
140
141
|
progress: (app, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map/progress`, p),
|
|
142
|
+
/** After npx plures check add pushed a branch with the check in it: Plures opens the pull request. Pass error instead if adding it failed. */
|
|
143
|
+
checkPr: (app, id, p) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/checks/${encodeURIComponent(id)}/pr`, p),
|
|
141
144
|
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
142
145
|
workflow: async (app, branch = "main") => {
|
|
143
146
|
const r = await fetch(`${this.base}/api/v1/map_workflow?app=${encodeURIComponent(app)}&branch=${encodeURIComponent(branch)}`);
|
|
@@ -195,6 +198,32 @@ var Plures = class {
|
|
|
195
198
|
};
|
|
196
199
|
/** Your prepaid credit. A live task that could cost more than available_cents is refused with a 402 insufficient_credits error. */
|
|
197
200
|
balance = () => this.req("GET", "/api/v1/balance");
|
|
201
|
+
/**
|
|
202
|
+
* Runs a check from your app's map and waits for the decision. Send the data by name; the check
|
|
203
|
+
* saved on Plures supplies the question, options, guidance, which fields people see (the rest
|
|
204
|
+
* never leave Plures), who decides, how often and what happens on a timeout. Any of those can be
|
|
205
|
+
* overridden for this call. `answer` is the option to act on: what people chose, the first option
|
|
206
|
+
* when the check wasn't needed (a sample or a condition) or timed out set to go ahead, and null
|
|
207
|
+
* when it timed out set to hold.
|
|
208
|
+
*/
|
|
209
|
+
async check(id, data = {}, overrides = {}, opts = {}) {
|
|
210
|
+
const t = await this.req("POST", `/api/v1/checks/${encodeURIComponent(id)}`, { data, ...overrides }, opts.idempotencyKey ? { "idempotency-key": opts.idempotencyKey } : {});
|
|
211
|
+
const final = t.status === "pending" ? await this.tasks.waitFor(t.id, { timeoutMs: opts.timeoutMs }) : t;
|
|
212
|
+
const opt = (k) => final.options.find((o) => o.id === k)?.label ?? k ?? null;
|
|
213
|
+
const first = final.options[0]?.label ?? null;
|
|
214
|
+
const timedOut = final.status === "expired" || final.status === "pending";
|
|
215
|
+
const answer = final.status === "completed" ? opt(final.result?.answer) : final.status === "skipped" ? first : timedOut && t.on_timeout === "proceed" ? first : null;
|
|
216
|
+
return {
|
|
217
|
+
...final,
|
|
218
|
+
check: t.check,
|
|
219
|
+
on_timeout: t.on_timeout,
|
|
220
|
+
kept_private: t.kept_private ?? [],
|
|
221
|
+
skipped_because: t.skipped_because ?? null,
|
|
222
|
+
answer,
|
|
223
|
+
checked: final.status !== "skipped",
|
|
224
|
+
timed_out: timedOut
|
|
225
|
+
};
|
|
226
|
+
}
|
|
198
227
|
/** Creates a task and waits for people to answer it. */
|
|
199
228
|
async verify(params, opts = {}) {
|
|
200
229
|
const t = await this.tasks.create(params, { idempotencyKey: opts.idempotencyKey });
|
package/dist/index.d.cts
CHANGED
|
@@ -4,6 +4,32 @@ type Option = {
|
|
|
4
4
|
};
|
|
5
5
|
/** "verifiers" asks ID verified people first; "team" goes straight to your own people ($0.05 per internal vote live). */
|
|
6
6
|
type Route = "verifiers" | "team";
|
|
7
|
+
type CheckOverrides = {
|
|
8
|
+
question?: string;
|
|
9
|
+
options?: string[];
|
|
10
|
+
instructions?: string;
|
|
11
|
+
verifiers?: number;
|
|
12
|
+
team?: string;
|
|
13
|
+
route?: "verifiers" | "team";
|
|
14
|
+
/** Check this one even if the sample or a condition would skip it. */ always?: boolean;
|
|
15
|
+
/** Share of calls people check, 0 to 1, for this call. */ sample?: number;
|
|
16
|
+
/** Seconds people have to answer. */ timeout?: number;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
webhook_url?: string;
|
|
19
|
+
};
|
|
20
|
+
type CheckExtra = {
|
|
21
|
+
check: string;
|
|
22
|
+
on_timeout: "proceed" | "hold";
|
|
23
|
+
kept_private?: string[];
|
|
24
|
+
skipped_because?: string;
|
|
25
|
+
};
|
|
26
|
+
type CheckResult = Task & Omit<CheckExtra, "kept_private" | "skipped_because"> & {
|
|
27
|
+
/** The option to act on, by label. */ answer: string | null;
|
|
28
|
+
/** false when the check wasn't needed this time (a sample or a condition). */ checked: boolean;
|
|
29
|
+
timed_out: boolean;
|
|
30
|
+
kept_private: string[];
|
|
31
|
+
skipped_because: string | null;
|
|
32
|
+
};
|
|
7
33
|
type TaskStatus = "pending" | "completed" | "expired" | "canceled" | "skipped";
|
|
8
34
|
type Result = {
|
|
9
35
|
answer: string | null;
|
|
@@ -253,6 +279,30 @@ type MapKey = {
|
|
|
253
279
|
created_at: string;
|
|
254
280
|
last_used_at: string | null;
|
|
255
281
|
};
|
|
282
|
+
type MapChanges = {
|
|
283
|
+
since: number | null;
|
|
284
|
+
added: {
|
|
285
|
+
id: string;
|
|
286
|
+
label: string;
|
|
287
|
+
score: number | null;
|
|
288
|
+
checked: boolean;
|
|
289
|
+
}[];
|
|
290
|
+
removed: {
|
|
291
|
+
id: string;
|
|
292
|
+
label: string;
|
|
293
|
+
}[];
|
|
294
|
+
renamed: {
|
|
295
|
+
from: string;
|
|
296
|
+
to: string;
|
|
297
|
+
label: string;
|
|
298
|
+
}[];
|
|
299
|
+
risky: {
|
|
300
|
+
id: string;
|
|
301
|
+
label: string;
|
|
302
|
+
score: number;
|
|
303
|
+
why: string[];
|
|
304
|
+
}[];
|
|
305
|
+
};
|
|
256
306
|
type StoredAgentMap = AgentMap & {
|
|
257
307
|
object: "agent_map";
|
|
258
308
|
app: string;
|
|
@@ -260,6 +310,23 @@ type StoredAgentMap = AgentMap & {
|
|
|
260
310
|
created_at?: string;
|
|
261
311
|
via?: string;
|
|
262
312
|
url?: string;
|
|
313
|
+
/** Set for a branch map: the branch, the main map version it's compared with, and its pull request. */
|
|
314
|
+
branch?: string;
|
|
315
|
+
main_version?: number | null;
|
|
316
|
+
pull_request?: string | null;
|
|
317
|
+
changes?: MapChanges;
|
|
318
|
+
engine?: {
|
|
319
|
+
version: string;
|
|
320
|
+
actions: Record<string, {
|
|
321
|
+
score: number;
|
|
322
|
+
factors: string[];
|
|
323
|
+
oversight: {
|
|
324
|
+
mode: string;
|
|
325
|
+
rate: number | null;
|
|
326
|
+
verifiers: number;
|
|
327
|
+
};
|
|
328
|
+
}>;
|
|
329
|
+
};
|
|
263
330
|
};
|
|
264
331
|
type Rubric = {
|
|
265
332
|
object: "rubric";
|
|
@@ -352,7 +419,7 @@ type WebhookEvent = {
|
|
|
352
419
|
created_at: string;
|
|
353
420
|
data: Task;
|
|
354
421
|
};
|
|
355
|
-
declare const VERSION = "1.
|
|
422
|
+
declare const VERSION = "1.16.0";
|
|
356
423
|
type GitHubLink = {
|
|
357
424
|
object: "github_link";
|
|
358
425
|
app: string;
|
|
@@ -469,7 +536,10 @@ declare class Plures {
|
|
|
469
536
|
guide: () => Promise<string>;
|
|
470
537
|
retrieve: (app: string) => Promise<StoredAgentMap>;
|
|
471
538
|
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
472
|
-
|
|
539
|
+
/** A branch other than the main one gets its own map, compared with main, and noted on its pull request. */
|
|
540
|
+
upload: (app: string, map: AgentMap, opts?: {
|
|
541
|
+
branch?: string;
|
|
542
|
+
}) => Promise<StoredAgentMap>;
|
|
473
543
|
setCheckpoint: (app: string, id: string, status: "added" | "dismissed" | "suggested") => Promise<StoredAgentMap>;
|
|
474
544
|
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
475
545
|
createKey: (app: string, opts?: {
|
|
@@ -525,6 +595,17 @@ declare class Plures {
|
|
|
525
595
|
phase: string;
|
|
526
596
|
received: number;
|
|
527
597
|
}>;
|
|
598
|
+
/** After npx plures check add pushed a branch with the check in it: Plures opens the pull request. Pass error instead if adding it failed. */
|
|
599
|
+
checkPr: (app: string, id: string, p: {
|
|
600
|
+
branch: string;
|
|
601
|
+
} | {
|
|
602
|
+
error: string;
|
|
603
|
+
}) => Promise<{
|
|
604
|
+
object: "check_pr";
|
|
605
|
+
check: string;
|
|
606
|
+
opened: boolean;
|
|
607
|
+
pull_request?: string;
|
|
608
|
+
}>;
|
|
528
609
|
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
529
610
|
workflow: (app: string, branch?: string) => Promise<string>;
|
|
530
611
|
};
|
|
@@ -654,6 +735,18 @@ declare class Plures {
|
|
|
654
735
|
};
|
|
655
736
|
/** Your prepaid credit. A live task that could cost more than available_cents is refused with a 402 insufficient_credits error. */
|
|
656
737
|
balance: () => Promise<Balance>;
|
|
738
|
+
/**
|
|
739
|
+
* Runs a check from your app's map and waits for the decision. Send the data by name; the check
|
|
740
|
+
* saved on Plures supplies the question, options, guidance, which fields people see (the rest
|
|
741
|
+
* never leave Plures), who decides, how often and what happens on a timeout. Any of those can be
|
|
742
|
+
* overridden for this call. `answer` is the option to act on: what people chose, the first option
|
|
743
|
+
* when the check wasn't needed (a sample or a condition) or timed out set to go ahead, and null
|
|
744
|
+
* when it timed out set to hold.
|
|
745
|
+
*/
|
|
746
|
+
check(id: string, data?: Record<string, unknown>, overrides?: CheckOverrides, opts?: {
|
|
747
|
+
timeoutMs?: number;
|
|
748
|
+
idempotencyKey?: string;
|
|
749
|
+
}): Promise<CheckResult>;
|
|
657
750
|
/** Creates a task and waits for people to answer it. */
|
|
658
751
|
verify(params: CreateTask, opts?: {
|
|
659
752
|
timeoutMs?: number;
|
|
@@ -667,4 +760,4 @@ declare class Plures {
|
|
|
667
760
|
static verifyWebhook(rawBody: string, signatureHeader: string | null, secret: string, toleranceSec?: number): Promise<WebhookEvent>;
|
|
668
761
|
}
|
|
669
762
|
|
|
670
|
-
export { type AgentMap, type App, type Balance, type Consensus, type CreateTask, type Escalation, type GitHubLink, type MapCheckpoint, type MapDraft, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, VERSION, type WebhookEvent, Plures as default };
|
|
763
|
+
export { type AgentMap, type App, type Balance, type CheckOverrides, type CheckResult, type Consensus, type CreateTask, type Escalation, type GitHubLink, type MapChanges, type MapCheckpoint, type MapDraft, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, VERSION, type WebhookEvent, Plures as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,32 @@ type Option = {
|
|
|
4
4
|
};
|
|
5
5
|
/** "verifiers" asks ID verified people first; "team" goes straight to your own people ($0.05 per internal vote live). */
|
|
6
6
|
type Route = "verifiers" | "team";
|
|
7
|
+
type CheckOverrides = {
|
|
8
|
+
question?: string;
|
|
9
|
+
options?: string[];
|
|
10
|
+
instructions?: string;
|
|
11
|
+
verifiers?: number;
|
|
12
|
+
team?: string;
|
|
13
|
+
route?: "verifiers" | "team";
|
|
14
|
+
/** Check this one even if the sample or a condition would skip it. */ always?: boolean;
|
|
15
|
+
/** Share of calls people check, 0 to 1, for this call. */ sample?: number;
|
|
16
|
+
/** Seconds people have to answer. */ timeout?: number;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
webhook_url?: string;
|
|
19
|
+
};
|
|
20
|
+
type CheckExtra = {
|
|
21
|
+
check: string;
|
|
22
|
+
on_timeout: "proceed" | "hold";
|
|
23
|
+
kept_private?: string[];
|
|
24
|
+
skipped_because?: string;
|
|
25
|
+
};
|
|
26
|
+
type CheckResult = Task & Omit<CheckExtra, "kept_private" | "skipped_because"> & {
|
|
27
|
+
/** The option to act on, by label. */ answer: string | null;
|
|
28
|
+
/** false when the check wasn't needed this time (a sample or a condition). */ checked: boolean;
|
|
29
|
+
timed_out: boolean;
|
|
30
|
+
kept_private: string[];
|
|
31
|
+
skipped_because: string | null;
|
|
32
|
+
};
|
|
7
33
|
type TaskStatus = "pending" | "completed" | "expired" | "canceled" | "skipped";
|
|
8
34
|
type Result = {
|
|
9
35
|
answer: string | null;
|
|
@@ -253,6 +279,30 @@ type MapKey = {
|
|
|
253
279
|
created_at: string;
|
|
254
280
|
last_used_at: string | null;
|
|
255
281
|
};
|
|
282
|
+
type MapChanges = {
|
|
283
|
+
since: number | null;
|
|
284
|
+
added: {
|
|
285
|
+
id: string;
|
|
286
|
+
label: string;
|
|
287
|
+
score: number | null;
|
|
288
|
+
checked: boolean;
|
|
289
|
+
}[];
|
|
290
|
+
removed: {
|
|
291
|
+
id: string;
|
|
292
|
+
label: string;
|
|
293
|
+
}[];
|
|
294
|
+
renamed: {
|
|
295
|
+
from: string;
|
|
296
|
+
to: string;
|
|
297
|
+
label: string;
|
|
298
|
+
}[];
|
|
299
|
+
risky: {
|
|
300
|
+
id: string;
|
|
301
|
+
label: string;
|
|
302
|
+
score: number;
|
|
303
|
+
why: string[];
|
|
304
|
+
}[];
|
|
305
|
+
};
|
|
256
306
|
type StoredAgentMap = AgentMap & {
|
|
257
307
|
object: "agent_map";
|
|
258
308
|
app: string;
|
|
@@ -260,6 +310,23 @@ type StoredAgentMap = AgentMap & {
|
|
|
260
310
|
created_at?: string;
|
|
261
311
|
via?: string;
|
|
262
312
|
url?: string;
|
|
313
|
+
/** Set for a branch map: the branch, the main map version it's compared with, and its pull request. */
|
|
314
|
+
branch?: string;
|
|
315
|
+
main_version?: number | null;
|
|
316
|
+
pull_request?: string | null;
|
|
317
|
+
changes?: MapChanges;
|
|
318
|
+
engine?: {
|
|
319
|
+
version: string;
|
|
320
|
+
actions: Record<string, {
|
|
321
|
+
score: number;
|
|
322
|
+
factors: string[];
|
|
323
|
+
oversight: {
|
|
324
|
+
mode: string;
|
|
325
|
+
rate: number | null;
|
|
326
|
+
verifiers: number;
|
|
327
|
+
};
|
|
328
|
+
}>;
|
|
329
|
+
};
|
|
263
330
|
};
|
|
264
331
|
type Rubric = {
|
|
265
332
|
object: "rubric";
|
|
@@ -352,7 +419,7 @@ type WebhookEvent = {
|
|
|
352
419
|
created_at: string;
|
|
353
420
|
data: Task;
|
|
354
421
|
};
|
|
355
|
-
declare const VERSION = "1.
|
|
422
|
+
declare const VERSION = "1.16.0";
|
|
356
423
|
type GitHubLink = {
|
|
357
424
|
object: "github_link";
|
|
358
425
|
app: string;
|
|
@@ -469,7 +536,10 @@ declare class Plures {
|
|
|
469
536
|
guide: () => Promise<string>;
|
|
470
537
|
retrieve: (app: string) => Promise<StoredAgentMap>;
|
|
471
538
|
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
472
|
-
|
|
539
|
+
/** A branch other than the main one gets its own map, compared with main, and noted on its pull request. */
|
|
540
|
+
upload: (app: string, map: AgentMap, opts?: {
|
|
541
|
+
branch?: string;
|
|
542
|
+
}) => Promise<StoredAgentMap>;
|
|
473
543
|
setCheckpoint: (app: string, id: string, status: "added" | "dismissed" | "suggested") => Promise<StoredAgentMap>;
|
|
474
544
|
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
475
545
|
createKey: (app: string, opts?: {
|
|
@@ -525,6 +595,17 @@ declare class Plures {
|
|
|
525
595
|
phase: string;
|
|
526
596
|
received: number;
|
|
527
597
|
}>;
|
|
598
|
+
/** After npx plures check add pushed a branch with the check in it: Plures opens the pull request. Pass error instead if adding it failed. */
|
|
599
|
+
checkPr: (app: string, id: string, p: {
|
|
600
|
+
branch: string;
|
|
601
|
+
} | {
|
|
602
|
+
error: string;
|
|
603
|
+
}) => Promise<{
|
|
604
|
+
object: "check_pr";
|
|
605
|
+
check: string;
|
|
606
|
+
opened: boolean;
|
|
607
|
+
pull_request?: string;
|
|
608
|
+
}>;
|
|
528
609
|
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
529
610
|
workflow: (app: string, branch?: string) => Promise<string>;
|
|
530
611
|
};
|
|
@@ -654,6 +735,18 @@ declare class Plures {
|
|
|
654
735
|
};
|
|
655
736
|
/** Your prepaid credit. A live task that could cost more than available_cents is refused with a 402 insufficient_credits error. */
|
|
656
737
|
balance: () => Promise<Balance>;
|
|
738
|
+
/**
|
|
739
|
+
* Runs a check from your app's map and waits for the decision. Send the data by name; the check
|
|
740
|
+
* saved on Plures supplies the question, options, guidance, which fields people see (the rest
|
|
741
|
+
* never leave Plures), who decides, how often and what happens on a timeout. Any of those can be
|
|
742
|
+
* overridden for this call. `answer` is the option to act on: what people chose, the first option
|
|
743
|
+
* when the check wasn't needed (a sample or a condition) or timed out set to go ahead, and null
|
|
744
|
+
* when it timed out set to hold.
|
|
745
|
+
*/
|
|
746
|
+
check(id: string, data?: Record<string, unknown>, overrides?: CheckOverrides, opts?: {
|
|
747
|
+
timeoutMs?: number;
|
|
748
|
+
idempotencyKey?: string;
|
|
749
|
+
}): Promise<CheckResult>;
|
|
657
750
|
/** Creates a task and waits for people to answer it. */
|
|
658
751
|
verify(params: CreateTask, opts?: {
|
|
659
752
|
timeoutMs?: number;
|
|
@@ -667,4 +760,4 @@ declare class Plures {
|
|
|
667
760
|
static verifyWebhook(rawBody: string, signatureHeader: string | null, secret: string, toleranceSec?: number): Promise<WebhookEvent>;
|
|
668
761
|
}
|
|
669
762
|
|
|
670
|
-
export { type AgentMap, type App, type Balance, type Consensus, type CreateTask, type Escalation, type GitHubLink, type MapCheckpoint, type MapDraft, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, VERSION, type WebhookEvent, Plures as default };
|
|
763
|
+
export { type AgentMap, type App, type Balance, type CheckOverrides, type CheckResult, type Consensus, type CreateTask, type Escalation, type GitHubLink, type MapChanges, type MapCheckpoint, type MapDraft, type MapKey, type MapNode, type Mix, type Option, Plures, PluresError, type Release, type Result, type Route, type Routing, type RoutingRule, type RoutingUpdate, type Rubric, type RubricParams, type Sample, type StoredAgentMap, type Task, type TaskStatus, type TeamTally, type TeamVote, type TestMix, VERSION, type WebhookEvent, Plures as default };
|
package/dist/index.js
CHANGED