anpord 0.1.19 → 0.1.20

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.
Files changed (43) hide show
  1. package/dist/api-context.d.cts +1 -1
  2. package/dist/api-context.d.mts +1 -1
  3. package/dist/{api-mocks-CAQFqUgY.d.cts → api-mocks-xXKzUFc9.d.cts} +2 -2
  4. package/dist/{api-mocks-CAQFqUgY.d.mts → api-mocks-xXKzUFc9.d.mts} +2 -2
  5. package/dist/api-runtime.d.cts +1 -1
  6. package/dist/api-runtime.d.mts +1 -1
  7. package/dist/api.d.cts +1 -1
  8. package/dist/api.d.mts +1 -1
  9. package/dist/bin.cjs +138 -20
  10. package/dist/bin.mjs +139 -21
  11. package/dist/{client-OTrC6QXZ.d.cts → client-BD2VyKja.d.cts} +864 -460
  12. package/dist/{client-BucAp4c0.d.mts → client-ClWUDcPC.d.mts} +864 -460
  13. package/dist/{client-COfGoWvI.cjs → client-D4SBJEsG.cjs} +24 -2
  14. package/dist/{client-sWrfA0jZ.mjs → client-DCSRP55Q.mjs} +24 -2
  15. package/dist/{compiler-q6rnLW2F.mjs → compiler-BKchnKps.mjs} +1 -1
  16. package/dist/{compiler-CPpRRAdH.cjs → compiler-DSN62KNe.cjs} +1 -1
  17. package/dist/config.cjs +1 -1
  18. package/dist/config.d.cts +1 -1
  19. package/dist/config.d.mts +1 -1
  20. package/dist/config.mjs +1 -1
  21. package/dist/{eval-turns-hf6tbL-E.mjs → eval-turns-DZTHit19.mjs} +1 -1
  22. package/dist/{eval-turns-Bv8wRQ-b.cjs → eval-turns-weR_7JIp.cjs} +1 -1
  23. package/dist/eval.cjs +1 -1
  24. package/dist/eval.d.cts +1 -1
  25. package/dist/eval.d.mts +1 -1
  26. package/dist/eval.mjs +1 -1
  27. package/dist/{evals-CSipRo7i.d.cts → evals-WMHOpPxd.d.cts} +16 -16
  28. package/dist/{evals-CSipRo7i.d.mts → evals-WMHOpPxd.d.mts} +16 -16
  29. package/dist/{evals-api-u98TxdqN.mjs → evals-api-B0lnr_R8.mjs} +56 -56
  30. package/dist/{evals-api-DZSr9rtP.cjs → evals-api-DuDr8UdV.cjs} +79 -55
  31. package/dist/index.cjs +2 -2
  32. package/dist/index.d.cts +3 -3
  33. package/dist/index.d.mts +3 -3
  34. package/dist/index.mjs +2 -2
  35. package/dist/source.d.cts +1 -1
  36. package/dist/source.d.mts +1 -1
  37. package/dist/{types-DCUo5on_.d.cts → types-CUVZ1A_F.d.cts} +11 -11
  38. package/dist/{types-LW0tX4MF.d.mts → types-Cw5k37Uk.d.mts} +11 -11
  39. package/dist/validator-runtime.d.cts +1 -1
  40. package/dist/validator-runtime.d.mts +1 -1
  41. package/dist/validators.cjs +1 -1
  42. package/dist/validators.mjs +1 -1
  43. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { t as ApiCall } from "./api-mocks-CAQFqUgY.cjs";
1
+ import { t as ApiCall } from "./api-mocks-xXKzUFc9.cjs";
2
2
  //#region src/mock-api/context.d.ts
3
3
  declare const apiContext: {
4
4
  url: (name: string) => Promise<string>;
@@ -1,4 +1,4 @@
1
- import { t as ApiCall } from "./api-mocks-CAQFqUgY.mjs";
1
+ import { t as ApiCall } from "./api-mocks-xXKzUFc9.mjs";
2
2
  //#region src/mock-api/context.d.ts
3
3
  declare const apiContext: {
4
4
  url: (name: string) => Promise<string>;
@@ -58,10 +58,10 @@ declare const ApiCall_base: Schema.Class<ApiCall, {
58
58
  }>>>;
59
59
  }>, never, {
60
60
  readonly api: string;
61
- } & {
62
- readonly startedAt: number;
63
61
  } & {
64
62
  readonly status: number;
63
+ } & {
64
+ readonly startedAt: number;
65
65
  } & {
66
66
  readonly path: string;
67
67
  } & {
@@ -58,10 +58,10 @@ declare const ApiCall_base: Schema.Class<ApiCall, {
58
58
  }>>>;
59
59
  }>, never, {
60
60
  readonly api: string;
61
- } & {
62
- readonly startedAt: number;
63
61
  } & {
64
62
  readonly status: number;
63
+ } & {
64
+ readonly startedAt: number;
65
65
  } & {
66
66
  readonly path: string;
67
67
  } & {
@@ -1,4 +1,4 @@
1
- import { t as ApiCall } from "./api-mocks-CAQFqUgY.cjs";
1
+ import { t as ApiCall } from "./api-mocks-xXKzUFc9.cjs";
2
2
  import { t as ApiDefinition } from "./define-NZvmQuIv.cjs";
3
3
  //#region src/mock-api/runtime.d.ts
4
4
  declare const withApi: <A>({ api, run }: {
@@ -1,4 +1,4 @@
1
- import { t as ApiCall } from "./api-mocks-CAQFqUgY.mjs";
1
+ import { t as ApiCall } from "./api-mocks-xXKzUFc9.mjs";
2
2
  import { t as ApiDefinition } from "./define-NZvmQuIv.mjs";
3
3
  //#region src/mock-api/runtime.d.ts
4
4
  declare const withApi: <A>({ api, run }: {
package/dist/api.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as ApiCall } from "./api-mocks-CAQFqUgY.cjs";
1
+ import { t as ApiCall } from "./api-mocks-xXKzUFc9.cjs";
2
2
  import { a as endpoint, i as api, n as ApiHandlerContext, r as EndpointDefinition, t as ApiDefinition } from "./define-NZvmQuIv.cjs";
3
3
  import { withApi } from "./api-runtime.cjs";
4
4
  export { type ApiCall, type ApiDefinition, type ApiHandlerContext, type EndpointDefinition, api, endpoint, withApi };
package/dist/api.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as ApiCall } from "./api-mocks-CAQFqUgY.mjs";
1
+ import { t as ApiCall } from "./api-mocks-xXKzUFc9.mjs";
2
2
  import { a as endpoint, i as api, n as ApiHandlerContext, r as EndpointDefinition, t as ApiDefinition } from "./define-NZvmQuIv.mjs";
3
3
  import { withApi } from "./api-runtime.mjs";
4
4
  export { type ApiCall, type ApiDefinition, type ApiHandlerContext, type EndpointDefinition, api, endpoint, withApi };
package/dist/bin.cjs CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- const require_client = require("./client-COfGoWvI.cjs");
3
- const require_evals_api = require("./evals-api-DZSr9rtP.cjs");
2
+ const require_client = require("./client-D4SBJEsG.cjs");
3
+ const require_evals_api = require("./evals-api-DuDr8UdV.cjs");
4
4
  const require_errors = require("./errors-BX1wry8K.cjs");
5
5
  const require_config = require("./config.cjs");
6
- const require_compiler = require("./compiler-CPpRRAdH.cjs");
6
+ const require_compiler = require("./compiler-DSN62KNe.cjs");
7
7
  let effect = require("effect");
8
8
  let _effect_platform = require("@effect/platform");
9
9
  let _effect_cli = require("@effect/cli");
10
10
  let _effect_platform_node = require("@effect/platform-node");
11
11
  let yaml = require("yaml");
12
12
  //#region package.json
13
- var version$1 = "0.1.19";
13
+ var version$1 = "0.1.20";
14
14
  //#endregion
15
15
  //#region ../template/src/extract.ts
16
16
  /** Reads escapes too, so text the renderer leaves literal is never reported as
@@ -21,6 +21,84 @@ function extractVariables(template) {
21
21
  return [...new Set(names)];
22
22
  }
23
23
  //#endregion
24
+ //#region src/cli/render.ts
25
+ const json = (value) => effect.Console.log(JSON.stringify(value, null, 2));
26
+ const promptContent = (prompt) => effect.Effect.sync(() => {
27
+ process.stdout.write(prompt.content);
28
+ if (!prompt.content.endsWith("\n")) process.stdout.write("\n");
29
+ });
30
+ const row = (line) => effect.Effect.sync(() => {
31
+ process.stdout.write(`${line}\n`);
32
+ });
33
+ const note = (message) => effect.Effect.sync(() => {
34
+ process.stderr.write(`${message}\n`);
35
+ });
36
+ const attended = effect.Effect.sync(() => globalThis.process?.stdout?.isTTY === true);
37
+ //#endregion
38
+ //#region src/cli/connector-command.ts
39
+ const asJson$2 = _effect_cli.Options.boolean("json").pipe(_effect_cli.Options.withDescription("Print the result as JSON"));
40
+ const integrationId = _effect_cli.Args.text({ name: "integration" }).pipe(_effect_cli.Args.withDescription("The integration to connect, such as openai"));
41
+ const connectorId = _effect_cli.Args.text({ name: "id" }).pipe(_effect_cli.Args.withDescription("The connector's id"));
42
+ const name = _effect_cli.Options.text("name").pipe(_effect_cli.Options.withDescription("What to call this connector"), _effect_cli.Options.optional);
43
+ const authMethod = _effect_cli.Options.text("auth").pipe(_effect_cli.Options.withDescription("Which auth method to use, when more than one exists"), _effect_cli.Options.optional);
44
+ const isDefault = _effect_cli.Options.boolean("default").pipe(_effect_cli.Options.withDescription("Make this the connector the runs use"));
45
+ const readSecret = (label) => effect.Effect.gen(function* () {
46
+ if (yield* attended) return effect.Redacted.value(yield* _effect_cli.Prompt.password({ message: label }));
47
+ return yield* effect.Effect.promise(async () => {
48
+ const chunks = [];
49
+ for await (const chunk of process.stdin) chunks.push(chunk);
50
+ return Buffer.concat(chunks).toString("utf8").trim();
51
+ });
52
+ });
53
+ const integrations = _effect_cli.Command.make("integrations", { asJson: asJson$2 }, ({ asJson: wantsJson }) => effect.Effect.gen(function* () {
54
+ const found = yield* (yield* require_client.AnpordApi).connectors.integrations({ payload: {} });
55
+ if (wantsJson) return yield* json(found);
56
+ return yield* effect.Effect.forEach(found, (integration) => row(`${integration.id}\t${integration.category}\t${integration.authMethods.map((method) => method.id).join(", ")}`));
57
+ })).pipe(_effect_cli.Command.withDescription("List the integrations that can be connected"));
58
+ const list$1 = _effect_cli.Command.make("list", { asJson: asJson$2 }, ({ asJson: wantsJson }) => effect.Effect.gen(function* () {
59
+ const found = yield* (yield* require_client.AnpordApi).connectors.list({ payload: {} });
60
+ if (wantsJson) return yield* json(found);
61
+ if (found.length === 0) return yield* note("No connectors yet. Add one with `anpord connectors add`.");
62
+ return yield* effect.Effect.forEach(found, (connector) => row(`${connector.id}\t${connector.integrationId}\t${connector.name}${connector.isDefault ? " (default)" : ""}`));
63
+ })).pipe(_effect_cli.Command.withDescription("List the connectors this organization has"));
64
+ const add = _effect_cli.Command.make("add", {
65
+ authMethod,
66
+ integrationId,
67
+ isDefault,
68
+ name
69
+ }, ({ authMethod: wantedMethod, integrationId: integration, isDefault: wantsDefault, name: wantedName }) => effect.Effect.gen(function* () {
70
+ const api = yield* require_client.AnpordApi;
71
+ const found = (yield* api.connectors.integrations({ payload: {} })).find((entry) => entry.id === integration);
72
+ if (found === void 0) return yield* note(`No integration called ${integration}. Run \`anpord connectors integrations\` to see them.`);
73
+ const method = effect.Option.match(wantedMethod, {
74
+ onNone: () => found.authMethods[0],
75
+ onSome: (wanted) => found.authMethods.find((entry) => entry.id === wanted)
76
+ });
77
+ if (method === void 0) return yield* note(`${integration} has no auth method called ${effect.Option.getOrElse(wantedMethod, () => "")}.`);
78
+ if (method.kind === "device") return yield* note(`${integration} signs in through a browser. Connect it at https://www.anpord.com/settings/connections.`);
79
+ const values = {};
80
+ for (const field of method.fields) values[field.name] = yield* readSecret(field.label);
81
+ const created = yield* api.connectors.add({ payload: {
82
+ authMethodId: method.id,
83
+ integrationId: integration,
84
+ isDefault: wantsDefault,
85
+ name: effect.Option.getOrElse(wantedName, () => found.label),
86
+ scope: "organization",
87
+ values
88
+ } });
89
+ return yield* note(`Connected ${created.integrationId} as ${created.id}`);
90
+ })).pipe(_effect_cli.Command.withDescription("Connect an integration. The secret is prompted for, or read from stdin"));
91
+ const remove = _effect_cli.Command.make("remove", { connectorId }, ({ connectorId: id }) => effect.Effect.gen(function* () {
92
+ yield* (yield* require_client.AnpordApi).connectors.remove({ payload: { id } });
93
+ return yield* note(`Removed ${id}`);
94
+ })).pipe(_effect_cli.Command.withDescription("Remove a connector"));
95
+ const connectors = _effect_cli.Command.make("connectors").pipe(_effect_cli.Command.withDescription("Connect the credentials an eval run needs"), _effect_cli.Command.withSubcommands([
96
+ add,
97
+ integrations,
98
+ list$1,
99
+ remove
100
+ ]));
101
+ //#endregion
24
102
  //#region src/cli/declarations.ts
25
103
  const PREAMBLE = [
26
104
  "// Generated by `anpord generate`. Do not edit.",
@@ -58,6 +136,32 @@ const evalFilesIn = (directory) => effect.Effect.gen(function* () {
58
136
  return (yield* (yield* _effect_platform.FileSystem.FileSystem).readDirectory(directory, { recursive: true })).filter(wanted).sort();
59
137
  }).pipe(effect.Effect.withSpan("Cli.evalFilesIn"));
60
138
  //#endregion
139
+ //#region src/cli/eval-verdict.ts
140
+ const MARKS = {
141
+ error: "!",
142
+ failed: "✗",
143
+ passed: "✓",
144
+ queued: "·",
145
+ running: "·",
146
+ skipped: "–"
147
+ };
148
+ const WIDTH = 72;
149
+ const clip$1 = (text) => {
150
+ const flat = text.replace(/\s+/g, " ").trim();
151
+ return flat.length > WIDTH ? `${flat.slice(0, 71)}…` : flat;
152
+ };
153
+ const decided = (validation) => validation.status === "passed" || validation.status === "failed";
154
+ const verdictLines = (trial) => {
155
+ const validations = trial.validations ?? [];
156
+ if (validations.length === 0) return [];
157
+ return validations.map((validation) => {
158
+ const mark = MARKS[validation.status];
159
+ const detail = validation.message === "" ? "" : ` ${clip$1(validation.message)}`;
160
+ return ` ${mark} ${validation.name}${detail}`;
161
+ });
162
+ };
163
+ const undecidedIn = (trial) => (trial.validations ?? []).filter((validation) => !decided(validation));
164
+ //#endregion
61
165
  //#region src/cli/eval-gate.ts
62
166
  const EvalGate = effect.Schema.Literal("strict", "never", "regressed", "unscored");
63
167
  const regressions = (run) => run.cells.filter((cell) => cell.comparison?.verdict === "regressed");
@@ -83,7 +187,12 @@ const problemsWith = (run, failOn, expected) => {
83
187
  return run.cells.flatMap((cell) => {
84
188
  if (cell.status !== "finished" || cell.trials.length === 0) return [`${cell.caseName} has no complete trial results.`];
85
189
  if (expected && cell.trials.length !== expected.trials) return [`${cell.caseName}: expected ${expected.trials} trials, received ${cell.trials.length}.`];
86
- return cell.trials.flatMap((trial) => trial.status === "passed" && trial.passed ? [] : [`${cell.caseName}, trial ${trial.ordinal}: ${trial.status}.`]);
190
+ return cell.trials.flatMap((trial) => {
191
+ if (trial.status === "passed" && trial.passed) return [];
192
+ const undecided = undecidedIn(trial);
193
+ const why = undecided.length === 0 ? "" : ` (${undecided.length} validator${undecided.length === 1 ? "" : "s"} never decided)`;
194
+ return [`${cell.caseName}, trial ${trial.ordinal}: ${trial.status}.${why}`, ...verdictLines(trial)];
195
+ });
87
196
  });
88
197
  }
89
198
  if (failOn === "never") return [];
@@ -102,20 +211,6 @@ var NoEvalFiles = class extends effect.Data.TaggedError("NoEvalFiles") {
102
211
  }
103
212
  };
104
213
  //#endregion
105
- //#region src/cli/render.ts
106
- const json = (value) => effect.Console.log(JSON.stringify(value, null, 2));
107
- const promptContent = (prompt) => effect.Effect.sync(() => {
108
- process.stdout.write(prompt.content);
109
- if (!prompt.content.endsWith("\n")) process.stdout.write("\n");
110
- });
111
- const row = (line) => effect.Effect.sync(() => {
112
- process.stdout.write(`${line}\n`);
113
- });
114
- const note = (message) => effect.Effect.sync(() => {
115
- process.stderr.write(`${message}\n`);
116
- });
117
- const attended = effect.Effect.sync(() => globalThis.process?.stdout?.isTTY === true);
118
- //#endregion
119
214
  //#region src/cli/eval-grid.ts
120
215
  const DIM = "\x1B[2m";
121
216
  const BOLD = "\x1B[1m";
@@ -150,6 +245,25 @@ const formatPassRate = (cell) => {
150
245
  const shown = `${Math.round(rate * PERCENT)}%`;
151
246
  return paint(rate === 1 ? GREEN : RED, shown);
152
247
  };
248
+ const TAIL = 56;
249
+ const clip = (text) => {
250
+ const flat = text.replace(/\s+/g, " ").trim();
251
+ return flat.length > TAIL ? `${flat.slice(0, 55)}…` : flat;
252
+ };
253
+ const latestOf = (cell) => {
254
+ const entry = cell.trials.find((trial) => trial.status === "running")?.trajectory.at(-1);
255
+ if (entry === void 0) return;
256
+ if (entry._tag === "command") return clip(entry.command);
257
+ if (entry._tag === "toolCall") return clip(entry.name);
258
+ return entry._tag === "fileChange" ? clip(entry.paths.join(" ")) : clip(entry.text);
259
+ };
260
+ const formatActivity = (cell) => {
261
+ const running = cell.trials.find((trial) => trial.status === "running");
262
+ if (running === void 0) return [];
263
+ const latest = latestOf(cell);
264
+ const counts = [`${running.commands} cmd`, ...running.filesChanged.length === 0 ? [] : [`${running.filesChanged.length} files`]].join(", ");
265
+ return [paint(DIM, ` ${counts}${latest === void 0 ? "" : ` ${latest}`}`)];
266
+ };
153
267
  const formatVariant = (run, cell) => {
154
268
  const task = run.tasks[cell.taskIndex];
155
269
  return task === void 0 ? "?" : `${task.harness}/${task.model}`;
@@ -160,7 +274,10 @@ const formatGrid = (run, trials, elapsedMs) => {
160
274
  const lines = [];
161
275
  for (const caseName of run.cases) {
162
276
  lines.push(` ${BOLD}${caseName}${RESET}`);
163
- for (const cell of run.cells.filter((one) => one.caseName === caseName)) lines.push(` ${formatStatus(cell)} ${formatVariant(run, cell).padEnd(width)} ${formatTrialProgress(cell, trials)} ${formatPassRate(cell)}`);
277
+ for (const cell of run.cells.filter((one) => one.caseName === caseName)) {
278
+ lines.push(` ${formatStatus(cell)} ${formatVariant(run, cell).padEnd(width)} ${formatTrialProgress(cell, trials)} ${formatPassRate(cell)}`);
279
+ lines.push(...formatActivity(cell));
280
+ }
164
281
  lines.push("");
165
282
  }
166
283
  lines.push(paint(DIM, ` ${formatElapsed(elapsedMs)} elapsed`));
@@ -891,6 +1008,7 @@ const gen = _effect_cli.Command.make("gen", { out }, writeDeclarations).pipe(_ef
891
1008
  const withClient = (command) => _effect_cli.Command.provide(command, require_config.ClientLayer);
892
1009
  const commands = [
893
1010
  runEval,
1011
+ withClient(connectors),
894
1012
  withClient(gen),
895
1013
  withClient(generate),
896
1014
  withClient(get),
package/dist/bin.mjs CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { a as ChannelName, o as PromptId, s as VersionNumber, t as AnpordApi } from "./client-sWrfA0jZ.mjs";
3
- import { f as EvalTrigger, i as EvalRun } from "./evals-api-u98TxdqN.mjs";
2
+ import { a as ChannelName, o as PromptId, s as VersionNumber, t as AnpordApi } from "./client-DCSRP55Q.mjs";
3
+ import { d as EvalTrigger, r as EvalRun } from "./evals-api-B0lnr_R8.mjs";
4
4
  import { i as tokenMatcher, r as asAnpordError } from "./errors-B0YknR5V.mjs";
5
5
  import { ClientLayer, webUrlConfig } from "./config.mjs";
6
- import { r as compileEvalEffect } from "./compiler-q6rnLW2F.mjs";
6
+ import { r as compileEvalEffect } from "./compiler-BKchnKps.mjs";
7
7
  import { Cause, Clock, Config, Console, Data, Duration, Effect, Layer, Option, ParseResult, Redacted, Ref, Schema } from "effect";
8
8
  import { FetchHttpClient, FileSystem, HttpClient, HttpClientError, HttpClientRequest } from "@effect/platform";
9
- import { Args, Command, Options } from "@effect/cli";
9
+ import { Args, Command, Options, Prompt } from "@effect/cli";
10
10
  import { NodeContext, NodeRuntime } from "@effect/platform-node";
11
11
  import { parseDocument } from "yaml";
12
12
  //#region package.json
13
- var version$1 = "0.1.19";
13
+ var version$1 = "0.1.20";
14
14
  //#endregion
15
15
  //#region ../template/src/extract.ts
16
16
  /** Reads escapes too, so text the renderer leaves literal is never reported as
@@ -21,6 +21,84 @@ function extractVariables(template) {
21
21
  return [...new Set(names)];
22
22
  }
23
23
  //#endregion
24
+ //#region src/cli/render.ts
25
+ const json = (value) => Console.log(JSON.stringify(value, null, 2));
26
+ const promptContent = (prompt) => Effect.sync(() => {
27
+ process.stdout.write(prompt.content);
28
+ if (!prompt.content.endsWith("\n")) process.stdout.write("\n");
29
+ });
30
+ const row = (line) => Effect.sync(() => {
31
+ process.stdout.write(`${line}\n`);
32
+ });
33
+ const note = (message) => Effect.sync(() => {
34
+ process.stderr.write(`${message}\n`);
35
+ });
36
+ const attended = Effect.sync(() => globalThis.process?.stdout?.isTTY === true);
37
+ //#endregion
38
+ //#region src/cli/connector-command.ts
39
+ const asJson$2 = Options.boolean("json").pipe(Options.withDescription("Print the result as JSON"));
40
+ const integrationId = Args.text({ name: "integration" }).pipe(Args.withDescription("The integration to connect, such as openai"));
41
+ const connectorId = Args.text({ name: "id" }).pipe(Args.withDescription("The connector's id"));
42
+ const name = Options.text("name").pipe(Options.withDescription("What to call this connector"), Options.optional);
43
+ const authMethod = Options.text("auth").pipe(Options.withDescription("Which auth method to use, when more than one exists"), Options.optional);
44
+ const isDefault = Options.boolean("default").pipe(Options.withDescription("Make this the connector the runs use"));
45
+ const readSecret = (label) => Effect.gen(function* () {
46
+ if (yield* attended) return Redacted.value(yield* Prompt.password({ message: label }));
47
+ return yield* Effect.promise(async () => {
48
+ const chunks = [];
49
+ for await (const chunk of process.stdin) chunks.push(chunk);
50
+ return Buffer.concat(chunks).toString("utf8").trim();
51
+ });
52
+ });
53
+ const integrations = Command.make("integrations", { asJson: asJson$2 }, ({ asJson: wantsJson }) => Effect.gen(function* () {
54
+ const found = yield* (yield* AnpordApi).connectors.integrations({ payload: {} });
55
+ if (wantsJson) return yield* json(found);
56
+ return yield* Effect.forEach(found, (integration) => row(`${integration.id}\t${integration.category}\t${integration.authMethods.map((method) => method.id).join(", ")}`));
57
+ })).pipe(Command.withDescription("List the integrations that can be connected"));
58
+ const list$1 = Command.make("list", { asJson: asJson$2 }, ({ asJson: wantsJson }) => Effect.gen(function* () {
59
+ const found = yield* (yield* AnpordApi).connectors.list({ payload: {} });
60
+ if (wantsJson) return yield* json(found);
61
+ if (found.length === 0) return yield* note("No connectors yet. Add one with `anpord connectors add`.");
62
+ return yield* Effect.forEach(found, (connector) => row(`${connector.id}\t${connector.integrationId}\t${connector.name}${connector.isDefault ? " (default)" : ""}`));
63
+ })).pipe(Command.withDescription("List the connectors this organization has"));
64
+ const add = Command.make("add", {
65
+ authMethod,
66
+ integrationId,
67
+ isDefault,
68
+ name
69
+ }, ({ authMethod: wantedMethod, integrationId: integration, isDefault: wantsDefault, name: wantedName }) => Effect.gen(function* () {
70
+ const api = yield* AnpordApi;
71
+ const found = (yield* api.connectors.integrations({ payload: {} })).find((entry) => entry.id === integration);
72
+ if (found === void 0) return yield* note(`No integration called ${integration}. Run \`anpord connectors integrations\` to see them.`);
73
+ const method = Option.match(wantedMethod, {
74
+ onNone: () => found.authMethods[0],
75
+ onSome: (wanted) => found.authMethods.find((entry) => entry.id === wanted)
76
+ });
77
+ if (method === void 0) return yield* note(`${integration} has no auth method called ${Option.getOrElse(wantedMethod, () => "")}.`);
78
+ if (method.kind === "device") return yield* note(`${integration} signs in through a browser. Connect it at https://www.anpord.com/settings/connections.`);
79
+ const values = {};
80
+ for (const field of method.fields) values[field.name] = yield* readSecret(field.label);
81
+ const created = yield* api.connectors.add({ payload: {
82
+ authMethodId: method.id,
83
+ integrationId: integration,
84
+ isDefault: wantsDefault,
85
+ name: Option.getOrElse(wantedName, () => found.label),
86
+ scope: "organization",
87
+ values
88
+ } });
89
+ return yield* note(`Connected ${created.integrationId} as ${created.id}`);
90
+ })).pipe(Command.withDescription("Connect an integration. The secret is prompted for, or read from stdin"));
91
+ const remove = Command.make("remove", { connectorId }, ({ connectorId: id }) => Effect.gen(function* () {
92
+ yield* (yield* AnpordApi).connectors.remove({ payload: { id } });
93
+ return yield* note(`Removed ${id}`);
94
+ })).pipe(Command.withDescription("Remove a connector"));
95
+ const connectors = Command.make("connectors").pipe(Command.withDescription("Connect the credentials an eval run needs"), Command.withSubcommands([
96
+ add,
97
+ integrations,
98
+ list$1,
99
+ remove
100
+ ]));
101
+ //#endregion
24
102
  //#region src/cli/declarations.ts
25
103
  const PREAMBLE = [
26
104
  "// Generated by `anpord generate`. Do not edit.",
@@ -58,6 +136,32 @@ const evalFilesIn = (directory) => Effect.gen(function* () {
58
136
  return (yield* (yield* FileSystem.FileSystem).readDirectory(directory, { recursive: true })).filter(wanted).sort();
59
137
  }).pipe(Effect.withSpan("Cli.evalFilesIn"));
60
138
  //#endregion
139
+ //#region src/cli/eval-verdict.ts
140
+ const MARKS = {
141
+ error: "!",
142
+ failed: "✗",
143
+ passed: "✓",
144
+ queued: "·",
145
+ running: "·",
146
+ skipped: "–"
147
+ };
148
+ const WIDTH = 72;
149
+ const clip$1 = (text) => {
150
+ const flat = text.replace(/\s+/g, " ").trim();
151
+ return flat.length > WIDTH ? `${flat.slice(0, 71)}…` : flat;
152
+ };
153
+ const decided = (validation) => validation.status === "passed" || validation.status === "failed";
154
+ const verdictLines = (trial) => {
155
+ const validations = trial.validations ?? [];
156
+ if (validations.length === 0) return [];
157
+ return validations.map((validation) => {
158
+ const mark = MARKS[validation.status];
159
+ const detail = validation.message === "" ? "" : ` ${clip$1(validation.message)}`;
160
+ return ` ${mark} ${validation.name}${detail}`;
161
+ });
162
+ };
163
+ const undecidedIn = (trial) => (trial.validations ?? []).filter((validation) => !decided(validation));
164
+ //#endregion
61
165
  //#region src/cli/eval-gate.ts
62
166
  const EvalGate = Schema.Literal("strict", "never", "regressed", "unscored");
63
167
  const regressions = (run) => run.cells.filter((cell) => cell.comparison?.verdict === "regressed");
@@ -83,7 +187,12 @@ const problemsWith = (run, failOn, expected) => {
83
187
  return run.cells.flatMap((cell) => {
84
188
  if (cell.status !== "finished" || cell.trials.length === 0) return [`${cell.caseName} has no complete trial results.`];
85
189
  if (expected && cell.trials.length !== expected.trials) return [`${cell.caseName}: expected ${expected.trials} trials, received ${cell.trials.length}.`];
86
- return cell.trials.flatMap((trial) => trial.status === "passed" && trial.passed ? [] : [`${cell.caseName}, trial ${trial.ordinal}: ${trial.status}.`]);
190
+ return cell.trials.flatMap((trial) => {
191
+ if (trial.status === "passed" && trial.passed) return [];
192
+ const undecided = undecidedIn(trial);
193
+ const why = undecided.length === 0 ? "" : ` (${undecided.length} validator${undecided.length === 1 ? "" : "s"} never decided)`;
194
+ return [`${cell.caseName}, trial ${trial.ordinal}: ${trial.status}.${why}`, ...verdictLines(trial)];
195
+ });
87
196
  });
88
197
  }
89
198
  if (failOn === "never") return [];
@@ -102,20 +211,6 @@ var NoEvalFiles = class extends Data.TaggedError("NoEvalFiles") {
102
211
  }
103
212
  };
104
213
  //#endregion
105
- //#region src/cli/render.ts
106
- const json = (value) => Console.log(JSON.stringify(value, null, 2));
107
- const promptContent = (prompt) => Effect.sync(() => {
108
- process.stdout.write(prompt.content);
109
- if (!prompt.content.endsWith("\n")) process.stdout.write("\n");
110
- });
111
- const row = (line) => Effect.sync(() => {
112
- process.stdout.write(`${line}\n`);
113
- });
114
- const note = (message) => Effect.sync(() => {
115
- process.stderr.write(`${message}\n`);
116
- });
117
- const attended = Effect.sync(() => globalThis.process?.stdout?.isTTY === true);
118
- //#endregion
119
214
  //#region src/cli/eval-grid.ts
120
215
  const DIM = "\x1B[2m";
121
216
  const BOLD = "\x1B[1m";
@@ -150,6 +245,25 @@ const formatPassRate = (cell) => {
150
245
  const shown = `${Math.round(rate * PERCENT)}%`;
151
246
  return paint(rate === 1 ? GREEN : RED, shown);
152
247
  };
248
+ const TAIL = 56;
249
+ const clip = (text) => {
250
+ const flat = text.replace(/\s+/g, " ").trim();
251
+ return flat.length > TAIL ? `${flat.slice(0, 55)}…` : flat;
252
+ };
253
+ const latestOf = (cell) => {
254
+ const entry = cell.trials.find((trial) => trial.status === "running")?.trajectory.at(-1);
255
+ if (entry === void 0) return;
256
+ if (entry._tag === "command") return clip(entry.command);
257
+ if (entry._tag === "toolCall") return clip(entry.name);
258
+ return entry._tag === "fileChange" ? clip(entry.paths.join(" ")) : clip(entry.text);
259
+ };
260
+ const formatActivity = (cell) => {
261
+ const running = cell.trials.find((trial) => trial.status === "running");
262
+ if (running === void 0) return [];
263
+ const latest = latestOf(cell);
264
+ const counts = [`${running.commands} cmd`, ...running.filesChanged.length === 0 ? [] : [`${running.filesChanged.length} files`]].join(", ");
265
+ return [paint(DIM, ` ${counts}${latest === void 0 ? "" : ` ${latest}`}`)];
266
+ };
153
267
  const formatVariant = (run, cell) => {
154
268
  const task = run.tasks[cell.taskIndex];
155
269
  return task === void 0 ? "?" : `${task.harness}/${task.model}`;
@@ -160,7 +274,10 @@ const formatGrid = (run, trials, elapsedMs) => {
160
274
  const lines = [];
161
275
  for (const caseName of run.cases) {
162
276
  lines.push(` ${BOLD}${caseName}${RESET}`);
163
- for (const cell of run.cells.filter((one) => one.caseName === caseName)) lines.push(` ${formatStatus(cell)} ${formatVariant(run, cell).padEnd(width)} ${formatTrialProgress(cell, trials)} ${formatPassRate(cell)}`);
277
+ for (const cell of run.cells.filter((one) => one.caseName === caseName)) {
278
+ lines.push(` ${formatStatus(cell)} ${formatVariant(run, cell).padEnd(width)} ${formatTrialProgress(cell, trials)} ${formatPassRate(cell)}`);
279
+ lines.push(...formatActivity(cell));
280
+ }
164
281
  lines.push("");
165
282
  }
166
283
  lines.push(paint(DIM, ` ${formatElapsed(elapsedMs)} elapsed`));
@@ -891,6 +1008,7 @@ const gen = Command.make("gen", { out }, writeDeclarations).pipe(Command.withDes
891
1008
  const withClient = (command) => Command.provide(command, ClientLayer);
892
1009
  const commands = [
893
1010
  runEval,
1011
+ withClient(connectors),
894
1012
  withClient(gen),
895
1013
  withClient(generate),
896
1014
  withClient(get),