plures 1.7.1 → 1.10.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 +2 -0
- package/dist/cli.cjs +137 -17
- package/dist/cli.js +293 -9
- package/dist/index.cjs +31 -2
- package/dist/index.d.cts +113 -2
- package/dist/index.d.ts +113 -2
- package/dist/index.js +196 -6
- package/package.json +1 -1
- package/dist/chunk-N4GCKRA6.js +0 -174
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.10.0";
|
|
30
30
|
var PluresError = class extends Error {
|
|
31
31
|
constructor(message, status, type) {
|
|
32
32
|
super(message);
|
|
@@ -128,7 +128,36 @@ var Plures = class {
|
|
|
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
|
upload: (app, map) => this.req("PUT", `/api/v1/apps/${encodeURIComponent(app)}/map`, map),
|
|
131
|
-
setCheckpoint: (app, id, status) => this.req("PATCH", `/api/v1/apps/${encodeURIComponent(app)}/map/checkpoints/${encodeURIComponent(id)}`, { status })
|
|
131
|
+
setCheckpoint: (app, id, status) => this.req("PATCH", `/api/v1/apps/${encodeURIComponent(app)}/map/checkpoints/${encodeURIComponent(id)}`, { status }),
|
|
132
|
+
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
133
|
+
createKey: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`, opts),
|
|
134
|
+
keys: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`),
|
|
135
|
+
revokeKey: (app, id) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/map_keys/${encodeURIComponent(id)}`),
|
|
136
|
+
/** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
|
|
137
|
+
startRun: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`, {}),
|
|
138
|
+
runs: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`),
|
|
139
|
+
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
140
|
+
workflow: async (app, branch = "main") => {
|
|
141
|
+
const r = await fetch(`${this.base}/api/v1/map_workflow?app=${encodeURIComponent(app)}&branch=${encodeURIComponent(branch)}`);
|
|
142
|
+
return r.text();
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Map with GitHub: Plures adds a workflow to your repo by pull request and keeps the app's map
|
|
147
|
+
* current on every push. Installing the Plures GitHub App needs a browser once (installUrl);
|
|
148
|
+
* everything after that works here.
|
|
149
|
+
*/
|
|
150
|
+
github = {
|
|
151
|
+
/** Repos the Plures GitHub App can reach. installed is false until you install it at install_url. */
|
|
152
|
+
repos: () => this.req("GET", "/api/v1/github/repos"),
|
|
153
|
+
/** Where an app's GitHub link stands: repo, pull request, mapping on, latest run, last map. */
|
|
154
|
+
status: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/github`),
|
|
155
|
+
/** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
|
|
156
|
+
connect: (app, repo) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github`, { repo }),
|
|
157
|
+
/** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
|
|
158
|
+
turnOn: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github/merge`, {}),
|
|
159
|
+
/** Revokes the repo's map key. Delete the workflow file in the repo to stop runs. */
|
|
160
|
+
disconnect: (app) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/github`)
|
|
132
161
|
};
|
|
133
162
|
/**
|
|
134
163
|
* Quality: people grade a sample of your agent's outputs against a rubric, so you see drift
|
package/dist/index.d.cts
CHANGED
|
@@ -242,6 +242,15 @@ type AgentMap = {
|
|
|
242
242
|
}[];
|
|
243
243
|
checkpoints: MapCheckpoint[];
|
|
244
244
|
};
|
|
245
|
+
type MapKey = {
|
|
246
|
+
object: "map_key";
|
|
247
|
+
id: string;
|
|
248
|
+
app: string;
|
|
249
|
+
prefix: string;
|
|
250
|
+
label: string | null;
|
|
251
|
+
created_at: string;
|
|
252
|
+
last_used_at: string | null;
|
|
253
|
+
};
|
|
245
254
|
type StoredAgentMap = AgentMap & {
|
|
246
255
|
object: "agent_map";
|
|
247
256
|
app: string;
|
|
@@ -341,7 +350,30 @@ type WebhookEvent = {
|
|
|
341
350
|
created_at: string;
|
|
342
351
|
data: Task;
|
|
343
352
|
};
|
|
344
|
-
declare const VERSION = "1.
|
|
353
|
+
declare const VERSION = "1.10.0";
|
|
354
|
+
type GitHubLink = {
|
|
355
|
+
object: "github_link";
|
|
356
|
+
app: string;
|
|
357
|
+
installed: boolean;
|
|
358
|
+
install_url: string;
|
|
359
|
+
connected: boolean;
|
|
360
|
+
repo?: string;
|
|
361
|
+
pull_request?: {
|
|
362
|
+
url: string | null;
|
|
363
|
+
state: "open" | "merged" | "closed" | "unknown";
|
|
364
|
+
};
|
|
365
|
+
mapping_on?: boolean;
|
|
366
|
+
mapped_by?: "plures" | "your_anthropic_key" | "needs_key";
|
|
367
|
+
first_map_free?: boolean;
|
|
368
|
+
latest_run?: {
|
|
369
|
+
status: string;
|
|
370
|
+
conclusion: string | null;
|
|
371
|
+
url: string;
|
|
372
|
+
created_at: string;
|
|
373
|
+
} | null;
|
|
374
|
+
mapped_at?: string | null;
|
|
375
|
+
connected_at?: string;
|
|
376
|
+
};
|
|
345
377
|
declare class PluresError extends Error {
|
|
346
378
|
readonly status: number;
|
|
347
379
|
readonly type: string;
|
|
@@ -437,6 +469,85 @@ declare class Plures {
|
|
|
437
469
|
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
438
470
|
upload: (app: string, map: AgentMap) => Promise<StoredAgentMap>;
|
|
439
471
|
setCheckpoint: (app: string, id: string, status: "added" | "dismissed" | "suggested") => Promise<StoredAgentMap>;
|
|
472
|
+
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
473
|
+
createKey: (app: string, opts?: {
|
|
474
|
+
label?: string;
|
|
475
|
+
}) => Promise<MapKey & {
|
|
476
|
+
key: string;
|
|
477
|
+
}>;
|
|
478
|
+
keys: (app: string) => Promise<{
|
|
479
|
+
object: "list";
|
|
480
|
+
data: MapKey[];
|
|
481
|
+
}>;
|
|
482
|
+
revokeKey: (app: string, id: string) => Promise<{
|
|
483
|
+
object: "map_key";
|
|
484
|
+
id: string;
|
|
485
|
+
deleted: true;
|
|
486
|
+
}>;
|
|
487
|
+
/** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
|
|
488
|
+
startRun: (app: string) => Promise<{
|
|
489
|
+
object: "map_run";
|
|
490
|
+
id: string;
|
|
491
|
+
app: string;
|
|
492
|
+
free: boolean;
|
|
493
|
+
max_cents: number;
|
|
494
|
+
token: string;
|
|
495
|
+
base_url: string;
|
|
496
|
+
model: string;
|
|
497
|
+
expires_in: number;
|
|
498
|
+
}>;
|
|
499
|
+
runs: (app: string) => Promise<{
|
|
500
|
+
object: "list";
|
|
501
|
+
data: {
|
|
502
|
+
object: "map_run";
|
|
503
|
+
id: string;
|
|
504
|
+
status: string;
|
|
505
|
+
free: boolean;
|
|
506
|
+
charged_cents: number | null;
|
|
507
|
+
created_at: string;
|
|
508
|
+
}[];
|
|
509
|
+
}>;
|
|
510
|
+
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
511
|
+
workflow: (app: string, branch?: string) => Promise<string>;
|
|
512
|
+
};
|
|
513
|
+
/**
|
|
514
|
+
* Map with GitHub: Plures adds a workflow to your repo by pull request and keeps the app's map
|
|
515
|
+
* current on every push. Installing the Plures GitHub App needs a browser once (installUrl);
|
|
516
|
+
* everything after that works here.
|
|
517
|
+
*/
|
|
518
|
+
github: {
|
|
519
|
+
/** Repos the Plures GitHub App can reach. installed is false until you install it at install_url. */
|
|
520
|
+
repos: () => Promise<{
|
|
521
|
+
object: "list";
|
|
522
|
+
installed: boolean;
|
|
523
|
+
install_url: string;
|
|
524
|
+
data: {
|
|
525
|
+
object: "github_repo";
|
|
526
|
+
repo: string;
|
|
527
|
+
private: boolean;
|
|
528
|
+
account: string;
|
|
529
|
+
default_branch: string;
|
|
530
|
+
}[];
|
|
531
|
+
}>;
|
|
532
|
+
/** Where an app's GitHub link stands: repo, pull request, mapping on, latest run, last map. */
|
|
533
|
+
status: (app: string) => Promise<GitHubLink>;
|
|
534
|
+
/** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
|
|
535
|
+
connect: (app: string, repo: string) => Promise<GitHubLink>;
|
|
536
|
+
/** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
|
|
537
|
+
turnOn: (app: string) => Promise<{
|
|
538
|
+
object: "github_merge";
|
|
539
|
+
app: string;
|
|
540
|
+
merged: true;
|
|
541
|
+
already_merged: boolean;
|
|
542
|
+
sha: string | null;
|
|
543
|
+
}>;
|
|
544
|
+
/** Revokes the repo's map key. Delete the workflow file in the repo to stop runs. */
|
|
545
|
+
disconnect: (app: string) => Promise<{
|
|
546
|
+
object: "github_link";
|
|
547
|
+
app: string;
|
|
548
|
+
connected: false;
|
|
549
|
+
deleted: true;
|
|
550
|
+
}>;
|
|
440
551
|
};
|
|
441
552
|
/**
|
|
442
553
|
* Quality: people grade a sample of your agent's outputs against a rubric, so you see drift
|
|
@@ -529,4 +640,4 @@ declare class Plures {
|
|
|
529
640
|
static verifyWebhook(rawBody: string, signatureHeader: string | null, secret: string, toleranceSec?: number): Promise<WebhookEvent>;
|
|
530
641
|
}
|
|
531
642
|
|
|
532
|
-
export { type AgentMap, type App, type Balance, type Consensus, type CreateTask, type Escalation, type MapCheckpoint, type MapDraft, 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 };
|
|
643
|
+
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -242,6 +242,15 @@ type AgentMap = {
|
|
|
242
242
|
}[];
|
|
243
243
|
checkpoints: MapCheckpoint[];
|
|
244
244
|
};
|
|
245
|
+
type MapKey = {
|
|
246
|
+
object: "map_key";
|
|
247
|
+
id: string;
|
|
248
|
+
app: string;
|
|
249
|
+
prefix: string;
|
|
250
|
+
label: string | null;
|
|
251
|
+
created_at: string;
|
|
252
|
+
last_used_at: string | null;
|
|
253
|
+
};
|
|
245
254
|
type StoredAgentMap = AgentMap & {
|
|
246
255
|
object: "agent_map";
|
|
247
256
|
app: string;
|
|
@@ -341,7 +350,30 @@ type WebhookEvent = {
|
|
|
341
350
|
created_at: string;
|
|
342
351
|
data: Task;
|
|
343
352
|
};
|
|
344
|
-
declare const VERSION = "1.
|
|
353
|
+
declare const VERSION = "1.10.0";
|
|
354
|
+
type GitHubLink = {
|
|
355
|
+
object: "github_link";
|
|
356
|
+
app: string;
|
|
357
|
+
installed: boolean;
|
|
358
|
+
install_url: string;
|
|
359
|
+
connected: boolean;
|
|
360
|
+
repo?: string;
|
|
361
|
+
pull_request?: {
|
|
362
|
+
url: string | null;
|
|
363
|
+
state: "open" | "merged" | "closed" | "unknown";
|
|
364
|
+
};
|
|
365
|
+
mapping_on?: boolean;
|
|
366
|
+
mapped_by?: "plures" | "your_anthropic_key" | "needs_key";
|
|
367
|
+
first_map_free?: boolean;
|
|
368
|
+
latest_run?: {
|
|
369
|
+
status: string;
|
|
370
|
+
conclusion: string | null;
|
|
371
|
+
url: string;
|
|
372
|
+
created_at: string;
|
|
373
|
+
} | null;
|
|
374
|
+
mapped_at?: string | null;
|
|
375
|
+
connected_at?: string;
|
|
376
|
+
};
|
|
345
377
|
declare class PluresError extends Error {
|
|
346
378
|
readonly status: number;
|
|
347
379
|
readonly type: string;
|
|
@@ -437,6 +469,85 @@ declare class Plures {
|
|
|
437
469
|
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
438
470
|
upload: (app: string, map: AgentMap) => Promise<StoredAgentMap>;
|
|
439
471
|
setCheckpoint: (app: string, id: string, status: "added" | "dismissed" | "suggested") => Promise<StoredAgentMap>;
|
|
472
|
+
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
473
|
+
createKey: (app: string, opts?: {
|
|
474
|
+
label?: string;
|
|
475
|
+
}) => Promise<MapKey & {
|
|
476
|
+
key: string;
|
|
477
|
+
}>;
|
|
478
|
+
keys: (app: string) => Promise<{
|
|
479
|
+
object: "list";
|
|
480
|
+
data: MapKey[];
|
|
481
|
+
}>;
|
|
482
|
+
revokeKey: (app: string, id: string) => Promise<{
|
|
483
|
+
object: "map_key";
|
|
484
|
+
id: string;
|
|
485
|
+
deleted: true;
|
|
486
|
+
}>;
|
|
487
|
+
/** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
|
|
488
|
+
startRun: (app: string) => Promise<{
|
|
489
|
+
object: "map_run";
|
|
490
|
+
id: string;
|
|
491
|
+
app: string;
|
|
492
|
+
free: boolean;
|
|
493
|
+
max_cents: number;
|
|
494
|
+
token: string;
|
|
495
|
+
base_url: string;
|
|
496
|
+
model: string;
|
|
497
|
+
expires_in: number;
|
|
498
|
+
}>;
|
|
499
|
+
runs: (app: string) => Promise<{
|
|
500
|
+
object: "list";
|
|
501
|
+
data: {
|
|
502
|
+
object: "map_run";
|
|
503
|
+
id: string;
|
|
504
|
+
status: string;
|
|
505
|
+
free: boolean;
|
|
506
|
+
charged_cents: number | null;
|
|
507
|
+
created_at: string;
|
|
508
|
+
}[];
|
|
509
|
+
}>;
|
|
510
|
+
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
511
|
+
workflow: (app: string, branch?: string) => Promise<string>;
|
|
512
|
+
};
|
|
513
|
+
/**
|
|
514
|
+
* Map with GitHub: Plures adds a workflow to your repo by pull request and keeps the app's map
|
|
515
|
+
* current on every push. Installing the Plures GitHub App needs a browser once (installUrl);
|
|
516
|
+
* everything after that works here.
|
|
517
|
+
*/
|
|
518
|
+
github: {
|
|
519
|
+
/** Repos the Plures GitHub App can reach. installed is false until you install it at install_url. */
|
|
520
|
+
repos: () => Promise<{
|
|
521
|
+
object: "list";
|
|
522
|
+
installed: boolean;
|
|
523
|
+
install_url: string;
|
|
524
|
+
data: {
|
|
525
|
+
object: "github_repo";
|
|
526
|
+
repo: string;
|
|
527
|
+
private: boolean;
|
|
528
|
+
account: string;
|
|
529
|
+
default_branch: string;
|
|
530
|
+
}[];
|
|
531
|
+
}>;
|
|
532
|
+
/** Where an app's GitHub link stands: repo, pull request, mapping on, latest run, last map. */
|
|
533
|
+
status: (app: string) => Promise<GitHubLink>;
|
|
534
|
+
/** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
|
|
535
|
+
connect: (app: string, repo: string) => Promise<GitHubLink>;
|
|
536
|
+
/** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
|
|
537
|
+
turnOn: (app: string) => Promise<{
|
|
538
|
+
object: "github_merge";
|
|
539
|
+
app: string;
|
|
540
|
+
merged: true;
|
|
541
|
+
already_merged: boolean;
|
|
542
|
+
sha: string | null;
|
|
543
|
+
}>;
|
|
544
|
+
/** Revokes the repo's map key. Delete the workflow file in the repo to stop runs. */
|
|
545
|
+
disconnect: (app: string) => Promise<{
|
|
546
|
+
object: "github_link";
|
|
547
|
+
app: string;
|
|
548
|
+
connected: false;
|
|
549
|
+
deleted: true;
|
|
550
|
+
}>;
|
|
440
551
|
};
|
|
441
552
|
/**
|
|
442
553
|
* Quality: people grade a sample of your agent's outputs against a rubric, so you see drift
|
|
@@ -529,4 +640,4 @@ declare class Plures {
|
|
|
529
640
|
static verifyWebhook(rawBody: string, signatureHeader: string | null, secret: string, toleranceSec?: number): Promise<WebhookEvent>;
|
|
530
641
|
}
|
|
531
642
|
|
|
532
|
-
export { type AgentMap, type App, type Balance, type Consensus, type CreateTask, type Escalation, type MapCheckpoint, type MapDraft, 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 };
|
|
643
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,199 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var VERSION = "1.10.0";
|
|
3
|
+
var PluresError = class extends Error {
|
|
4
|
+
constructor(message, status, type) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.status = status;
|
|
7
|
+
this.type = type;
|
|
8
|
+
}
|
|
9
|
+
status;
|
|
10
|
+
type;
|
|
11
|
+
};
|
|
12
|
+
var Plures = class {
|
|
13
|
+
base;
|
|
14
|
+
apiKey;
|
|
15
|
+
/** The app tasks go to when they don't name one. From opts.app or PLURES_APP. */
|
|
16
|
+
app;
|
|
17
|
+
ua;
|
|
18
|
+
constructor(apiKey, opts = {}) {
|
|
19
|
+
const key = apiKey ?? (typeof process !== "undefined" ? process.env?.PLURES_API_KEY : void 0);
|
|
20
|
+
if (!key) throw new Error("Pass your Plures API key, or set PLURES_API_KEY. Get a free test key at https://plures.si/?for=developers#join");
|
|
21
|
+
this.apiKey = key;
|
|
22
|
+
this.ua = opts.userAgent ?? `plures-js/${VERSION}`;
|
|
23
|
+
this.app = opts.app ?? (typeof process !== "undefined" ? process.env?.PLURES_APP : void 0) ?? null;
|
|
24
|
+
this.base = (opts.baseUrl ?? (typeof process !== "undefined" ? process.env?.PLURES_BASE_URL : void 0) ?? "https://plures.si").replace(/\/$/, "");
|
|
25
|
+
}
|
|
26
|
+
async req(method, path, body, headers = {}) {
|
|
27
|
+
for (let attempt = 0; ; attempt++) {
|
|
28
|
+
const r = await fetch(this.base + path, {
|
|
29
|
+
method,
|
|
30
|
+
headers: { authorization: `Bearer ${this.apiKey}`, "user-agent": this.ua, ...body ? { "content-type": "application/json" } : {}, ...this.app ? { "plures-app": this.app } : {}, ...headers },
|
|
31
|
+
body: body ? JSON.stringify(body) : void 0
|
|
32
|
+
});
|
|
33
|
+
if (r.status >= 500 && attempt < 2) {
|
|
34
|
+
await sleep(500 * 2 ** attempt);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const data = await r.json().catch(() => ({}));
|
|
38
|
+
if (!r.ok) throw new PluresError(data?.error?.message ?? `HTTP ${r.status}`, r.status, data?.error?.type ?? "api_error");
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
tasks = {
|
|
43
|
+
/** Sends a question to verified people. Pass an idempotency key to make retries safe. */
|
|
44
|
+
create: (params, opts = {}) => this.req("POST", "/api/v1/tasks", params, opts.idempotencyKey ? { "idempotency-key": opts.idempotencyKey } : {}),
|
|
45
|
+
retrieve: (id) => this.req("GET", `/api/v1/tasks/${encodeURIComponent(id)}`),
|
|
46
|
+
list: (params = {}) => {
|
|
47
|
+
const q = new URLSearchParams(Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)]));
|
|
48
|
+
return this.req("GET", `/api/v1/tasks${q.size ? `?${q}` : ""}`);
|
|
49
|
+
},
|
|
50
|
+
cancel: (id) => this.req("POST", `/api/v1/tasks/${encodeURIComponent(id)}/cancel`),
|
|
51
|
+
/** Polls until the task is settled. Prefer webhooks in production. */
|
|
52
|
+
waitFor: async (id, opts = {}) => {
|
|
53
|
+
const end = Date.now() + (opts.timeoutMs ?? 10 * 6e4);
|
|
54
|
+
for (; ; ) {
|
|
55
|
+
const t = await this.tasks.retrieve(id);
|
|
56
|
+
if (t.status !== "pending") return t;
|
|
57
|
+
if (Date.now() > end) return t;
|
|
58
|
+
await sleep(opts.intervalMs ?? 3e3);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Test keys only. How test tasks behave when they don't set metadata.test_outcome,
|
|
64
|
+
* so an agent running unchanged sends a share of decisions to your team.
|
|
65
|
+
* A single task can override it with metadata.test_mix.
|
|
66
|
+
*/
|
|
67
|
+
testMix = {
|
|
68
|
+
get: () => this.req("GET", "/api/v1/test_mix"),
|
|
69
|
+
/** For example { unanimous: 70, split: 20, tie: 5, expired: 5 }. Pass null to turn it off. */
|
|
70
|
+
set: (mix) => this.req("PUT", "/api/v1/test_mix", { mix }),
|
|
71
|
+
clear: () => this.req("DELETE", "/api/v1/test_mix")
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Where tasks go. set() replaces what you send (team, groups or rules) and keeps the rest.
|
|
75
|
+
* For example { groups: [{ name: "Finance", emails: ["ap@acme.com"] }], rules: [{ category: "refunds", route: "team", group: "Finance" }] }.
|
|
76
|
+
*/
|
|
77
|
+
routing = {
|
|
78
|
+
get: () => this.req("GET", "/api/v1/routing"),
|
|
79
|
+
set: (update) => this.req("PUT", "/api/v1/routing", update)
|
|
80
|
+
};
|
|
81
|
+
/** Your apps. Each agent you run can be its own app, with its own tasks, ledger view and map. */
|
|
82
|
+
apps = {
|
|
83
|
+
list: () => this.req("GET", "/api/v1/apps"),
|
|
84
|
+
/** { name: "Support agent" }, with an optional id like "support-agent". */
|
|
85
|
+
create: (params) => this.req("POST", "/api/v1/apps", params),
|
|
86
|
+
retrieve: (id) => this.req("GET", `/api/v1/apps/${encodeURIComponent(id)}`),
|
|
87
|
+
/** Rename it, change its id (its tasks move with it), or make it the default. */
|
|
88
|
+
update: (id, params) => this.req("PATCH", `/api/v1/apps/${encodeURIComponent(id)}`, params),
|
|
89
|
+
/** Only an app with no tasks can be deleted. */
|
|
90
|
+
del: (id) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(id)}`)
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Agent maps: where each app's agent needs a person. Your coding agent reads your code and
|
|
94
|
+
* writes the map; Plures gets only the map. guide() returns the steps it follows.
|
|
95
|
+
*/
|
|
96
|
+
maps = {
|
|
97
|
+
guide: async () => {
|
|
98
|
+
const r = await fetch(`${this.base}/api/v1/map_guide`);
|
|
99
|
+
return r.text();
|
|
100
|
+
},
|
|
101
|
+
retrieve: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map`),
|
|
102
|
+
/** Uploads a new version. Checks you already added or dismissed keep that. */
|
|
103
|
+
upload: (app, map) => this.req("PUT", `/api/v1/apps/${encodeURIComponent(app)}/map`, map),
|
|
104
|
+
setCheckpoint: (app, id, status) => this.req("PATCH", `/api/v1/apps/${encodeURIComponent(app)}/map/checkpoints/${encodeURIComponent(id)}`, { status }),
|
|
105
|
+
/** A key that can only read and upload this app's map, for CI. The key is in the response once. */
|
|
106
|
+
createKey: (app, opts = {}) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`, opts),
|
|
107
|
+
keys: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_keys`),
|
|
108
|
+
revokeKey: (app, id) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/map_keys/${encodeURIComponent(id)}`),
|
|
109
|
+
/** Starts a map run where Plures runs Claude (CI with no Anthropic key). npx plures map does this. */
|
|
110
|
+
startRun: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`, {}),
|
|
111
|
+
runs: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/map_runs`),
|
|
112
|
+
/** The GitHub Actions workflow that maps a repo on every push to its main branch. */
|
|
113
|
+
workflow: async (app, branch = "main") => {
|
|
114
|
+
const r = await fetch(`${this.base}/api/v1/map_workflow?app=${encodeURIComponent(app)}&branch=${encodeURIComponent(branch)}`);
|
|
115
|
+
return r.text();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Map with GitHub: Plures adds a workflow to your repo by pull request and keeps the app's map
|
|
120
|
+
* current on every push. Installing the Plures GitHub App needs a browser once (installUrl);
|
|
121
|
+
* everything after that works here.
|
|
122
|
+
*/
|
|
123
|
+
github = {
|
|
124
|
+
/** Repos the Plures GitHub App can reach. installed is false until you install it at install_url. */
|
|
125
|
+
repos: () => this.req("GET", "/api/v1/github/repos"),
|
|
126
|
+
/** Where an app's GitHub link stands: repo, pull request, mapping on, latest run, last map. */
|
|
127
|
+
status: (app) => this.req("GET", `/api/v1/apps/${encodeURIComponent(app)}/github`),
|
|
128
|
+
/** Connects a repo ("owner/name"): sets its map key secret and opens the pull request. */
|
|
129
|
+
connect: (app, repo) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github`, { repo }),
|
|
130
|
+
/** Turns mapping on by merging the pull request. The repo's own rules (reviews, checks) still apply. */
|
|
131
|
+
turnOn: (app) => this.req("POST", `/api/v1/apps/${encodeURIComponent(app)}/github/merge`, {}),
|
|
132
|
+
/** Revokes the repo's map key. Delete the workflow file in the repo to stop runs. */
|
|
133
|
+
disconnect: (app) => this.req("DELETE", `/api/v1/apps/${encodeURIComponent(app)}/github`)
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Quality: people grade a sample of your agent's outputs against a rubric, so you see drift
|
|
137
|
+
* early, and compare two versions blind before you ship. Personal details are taken out first.
|
|
138
|
+
*/
|
|
139
|
+
quality = {
|
|
140
|
+
/** Send every output; the rubric's sample rate decides which ones people grade. */
|
|
141
|
+
sample: (p) => this.req("POST", "/api/v1/samples", p),
|
|
142
|
+
/** A blind release check: a is the current version's output, b the new one's, for the same input. */
|
|
143
|
+
compare: (p) => this.req("POST", "/api/v1/comparisons", p),
|
|
144
|
+
/** How often people preferred the new version, with a 95% range and a verdict. */
|
|
145
|
+
release: (name, opts = {}) => this.req("GET", `/api/v1/releases/${encodeURIComponent(name)}${opts.app ? `?app=${encodeURIComponent(opts.app)}` : ""}`),
|
|
146
|
+
rubrics: {
|
|
147
|
+
list: (opts = {}) => this.req("GET", `/api/v1/rubrics${opts.app ? `?app=${encodeURIComponent(opts.app)}` : ""}`),
|
|
148
|
+
/** { name: "reply_quality", question: "Is this reply correct and kind?", options: ["Good", "Okay", "Bad"], sample_rate: 0.05 } */
|
|
149
|
+
create: (p) => this.req("POST", "/api/v1/rubrics", p),
|
|
150
|
+
retrieve: (name, opts = {}) => this.req("GET", `/api/v1/rubrics/${encodeURIComponent(name)}${opts.app ? `?app=${encodeURIComponent(opts.app)}` : ""}`),
|
|
151
|
+
update: (name, p) => this.req("PATCH", `/api/v1/rubrics/${encodeURIComponent(name)}${p.app ? `?app=${encodeURIComponent(p.app)}` : ""}`, p),
|
|
152
|
+
del: (name, opts = {}) => this.req("DELETE", `/api/v1/rubrics/${encodeURIComponent(name)}${opts.app ? `?app=${encodeURIComponent(opts.app)}` : ""}`)
|
|
153
|
+
},
|
|
154
|
+
/** Graded samples as JSON lines (input, output, grade, score, agreement), for fine tuning or an eval set. */
|
|
155
|
+
export: async (rubric, opts = {}) => {
|
|
156
|
+
const q = new URLSearchParams(Object.entries({ app: opts.app ?? this.app ?? void 0, since: opts.since }).filter(([, v]) => v));
|
|
157
|
+
const r = await fetch(`${this.base}/api/v1/rubrics/${encodeURIComponent(rubric)}/export${q.size ? `?${q}` : ""}`, { headers: { authorization: `Bearer ${this.apiKey}`, "user-agent": this.ua } });
|
|
158
|
+
if (!r.ok) {
|
|
159
|
+
const d = await r.json().catch(() => ({}));
|
|
160
|
+
throw new PluresError(d?.error?.message ?? `HTTP ${r.status}`, r.status, d?.error?.type ?? "api_error");
|
|
161
|
+
}
|
|
162
|
+
return (await r.text()).split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
/** Your prepaid credit. A live task that could cost more than available_cents is refused with a 402 insufficient_credits error. */
|
|
166
|
+
balance = () => this.req("GET", "/api/v1/balance");
|
|
167
|
+
/** Creates a task and waits for people to answer it. */
|
|
168
|
+
async verify(params, opts = {}) {
|
|
169
|
+
const t = await this.tasks.create(params, { idempotencyKey: opts.idempotencyKey });
|
|
170
|
+
return t.status === "pending" ? this.tasks.waitFor(t.id, { timeoutMs: opts.timeoutMs }) : t;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Checks the Plures-Signature header on a webhook and returns the parsed event.
|
|
174
|
+
* Pass the raw request body exactly as received. Throws if the signature is wrong
|
|
175
|
+
* or older than toleranceSec.
|
|
176
|
+
*/
|
|
177
|
+
static async verifyWebhook(rawBody, signatureHeader, secret, toleranceSec = 300) {
|
|
178
|
+
const parts = Object.fromEntries((signatureHeader ?? "").split(",").map((p) => p.split("=", 2)));
|
|
179
|
+
const t = Number(parts.t);
|
|
180
|
+
if (!t || !parts.v1) throw new Error("Missing Plures-Signature header");
|
|
181
|
+
if (Math.abs(Date.now() / 1e3 - t) > toleranceSec) throw new Error("Webhook timestamp is too old");
|
|
182
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
183
|
+
const mac = new Uint8Array(await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(`${t}.${rawBody}`)));
|
|
184
|
+
const expected = Array.from(mac, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
185
|
+
if (!timingSafeEqual(expected, parts.v1)) throw new Error("Webhook signature does not match");
|
|
186
|
+
return JSON.parse(rawBody);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
function timingSafeEqual(a, b) {
|
|
190
|
+
if (a.length !== b.length) return false;
|
|
191
|
+
let diff = 0;
|
|
192
|
+
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
193
|
+
return diff === 0;
|
|
194
|
+
}
|
|
195
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
196
|
+
var index_default = Plures;
|
|
7
197
|
export {
|
|
8
198
|
Plures,
|
|
9
199
|
PluresError,
|