@withgauge/cli 0.12.0 → 0.12.1
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 +18 -0
- package/dist/index.js +268 -112
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -215,6 +215,24 @@ Edit a skill with `--edit-skill <versionId> --skill-dir <path>`: the directory
|
|
|
215
215
|
holds the complete bundle, `SKILL.md` included. `trials template --kind <kind>`
|
|
216
216
|
prints a skeleton for the `--file` form.
|
|
217
217
|
|
|
218
|
+
A chat-guided round, such as a CLI change, goes through preview and save. Save
|
|
219
|
+
replaces the pending drafts, so it can fix a draft that is wrong:
|
|
220
|
+
|
|
221
|
+
```sh
|
|
222
|
+
gauge optimizations authoring cl_1 > context.json # revision, CLI targets, invocations
|
|
223
|
+
gauge optimizations invocation cl_1 inv_1 # one recorded baseline invocation
|
|
224
|
+
gauge optimizations cli-change cl_1 --file change.json > bundle.json
|
|
225
|
+
# round.json: {"revision": "...", "trials": [{"trial": {"title": "...",
|
|
226
|
+
# "lever": "CLI", "intervention": {"kind": "CLI_INTERVENTION", "after": <bundle.json>}}}]}
|
|
227
|
+
gauge optimizations round preview cl_1 --file round.json # prints previewDigest
|
|
228
|
+
gauge optimizations round save cl_1 --file round.json --digest <previewDigest>
|
|
229
|
+
gauge optimizations trials remove cl_1 R1.2 --yes # drop one draft
|
|
230
|
+
gauge optimizations launch cl_1 --yes
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
A `completeOutput` literal matcher is an exact, case-sensitive match. Copy it
|
|
234
|
+
from the recorded command output.
|
|
235
|
+
|
|
218
236
|
Every detail carries `nextAction` — `START`, `WAIT`, `AUTHOR_TRIAL`,
|
|
219
237
|
`LAUNCH_ROUND`, `ADOPT`, `RESUME`, or `NONE`. Read it (`get -o json`) instead of
|
|
220
238
|
inferring whose turn it is from round and trial statuses. `watch` exits `4` when
|
package/dist/index.js
CHANGED
|
@@ -2568,9 +2568,6 @@ var meResponseSchema = z27.object({
|
|
|
2568
2568
|
// ../packages/api-schemas/src/optimizations.ts
|
|
2569
2569
|
import { z as z31 } from "zod";
|
|
2570
2570
|
|
|
2571
|
-
// ../packages/api-schemas/src/climb.ts
|
|
2572
|
-
import { z as z30 } from "zod";
|
|
2573
|
-
|
|
2574
2571
|
// ../packages/api-schemas/src/cli-interventions.ts
|
|
2575
2572
|
import { z as z28 } from "zod";
|
|
2576
2573
|
var digest = z28.string().regex(/^[a-f0-9]{64}$/);
|
|
@@ -2598,12 +2595,47 @@ var cliLaunchFormSchema = z28.enum([
|
|
|
2598
2595
|
"execveat-path",
|
|
2599
2596
|
"execveat-fd"
|
|
2600
2597
|
]);
|
|
2598
|
+
var cliObservationReasonSchema = z28.enum([
|
|
2599
|
+
"eligible",
|
|
2600
|
+
"other-target",
|
|
2601
|
+
"other-command",
|
|
2602
|
+
"selector-mismatch",
|
|
2603
|
+
"unknown-command",
|
|
2604
|
+
"identity-unresolved",
|
|
2605
|
+
"artifact-mismatch",
|
|
2606
|
+
"runtime-mismatch",
|
|
2607
|
+
"launch-form",
|
|
2608
|
+
"io-profile",
|
|
2609
|
+
"operation-profile",
|
|
2610
|
+
"descriptor-lifetime"
|
|
2611
|
+
]);
|
|
2601
2612
|
var cliIoProfileSchema = z28.enum([
|
|
2602
2613
|
"pipes",
|
|
2603
2614
|
"redirected",
|
|
2604
2615
|
"shared-descriptor",
|
|
2605
2616
|
"pty"
|
|
2606
2617
|
]);
|
|
2618
|
+
var cliObservationSchema = z28.object({
|
|
2619
|
+
invocationId: z28.string().regex(/^[a-f0-9]{32}$/),
|
|
2620
|
+
targetId: token,
|
|
2621
|
+
commandId: token.nullable(),
|
|
2622
|
+
entrypointSha256: digest,
|
|
2623
|
+
runtimeSha256: digest.nullable(),
|
|
2624
|
+
launchForm: cliLaunchFormSchema.nullable(),
|
|
2625
|
+
ioProfile: cliIoProfileSchema.nullable(),
|
|
2626
|
+
reason: cliObservationReasonSchema
|
|
2627
|
+
});
|
|
2628
|
+
var cliCoverageSchema = z28.object({
|
|
2629
|
+
attempt: z28.number().int().nonnegative(),
|
|
2630
|
+
observedInvocationCount: z28.number().int().nonnegative().nullable(),
|
|
2631
|
+
invocationCount: z28.number().int().nonnegative(),
|
|
2632
|
+
appliedInvocationCount: z28.number().int().nonnegative(),
|
|
2633
|
+
incompleteCount: z28.number().int().nonnegative(),
|
|
2634
|
+
unsupportedCount: z28.number().int().nonnegative(),
|
|
2635
|
+
instrumentationFailed: z28.boolean(),
|
|
2636
|
+
observations: z28.array(cliObservationSchema),
|
|
2637
|
+
unsupportedDiagnostics: z28.array(z28.string())
|
|
2638
|
+
});
|
|
2607
2639
|
var cliOperationKindSchema = z28.enum([
|
|
2608
2640
|
"ensureOption",
|
|
2609
2641
|
"replaceOption",
|
|
@@ -2866,12 +2898,14 @@ var baselineInvocationSchema = z28.object({
|
|
|
2866
2898
|
artifactSha256: digest.nullable(),
|
|
2867
2899
|
entrypointSha256: digest,
|
|
2868
2900
|
engineSha256: digest.optional(),
|
|
2901
|
+
runtimeSha256: digest.optional(),
|
|
2869
2902
|
launchForm: cliLaunchFormSchema,
|
|
2870
2903
|
ioProfile: cliIoProfileSchema,
|
|
2871
2904
|
recordFiles: z28.array(cliBaselineRecordFileSchema).max(50).default([])
|
|
2872
2905
|
}).strict();
|
|
2873
2906
|
var cliInterventionSchema = z28.object({
|
|
2874
2907
|
schemaVersion: z28.union([z28.literal(1), z28.literal(2)]),
|
|
2908
|
+
identityPolicy: z28.literal("artifact").optional(),
|
|
2875
2909
|
engineVersion: token,
|
|
2876
2910
|
engineSha256: digest.nullable(),
|
|
2877
2911
|
bundleSha256: digest,
|
|
@@ -2904,6 +2938,13 @@ var cliInterventionSchema = z28.object({
|
|
|
2904
2938
|
const fail = (path2, message) => ctx.addIssue({ code: "custom", path: path2, message });
|
|
2905
2939
|
if (bundle.schemaVersion !== bundle.catalog.version)
|
|
2906
2940
|
fail(["schemaVersion"], "Bundle and catalog schema versions differ");
|
|
2941
|
+
if (bundle.identityPolicy && bundle.schemaVersion !== 2)
|
|
2942
|
+
fail(["identityPolicy"], "Artifact identity requires schema version 2");
|
|
2943
|
+
if (bundle.identityPolicy && bundle.baselineInvocations.some((row2) => !row2.runtimeSha256))
|
|
2944
|
+
fail(
|
|
2945
|
+
["baselineInvocations"],
|
|
2946
|
+
"Artifact identity requires the recorded runtime bytes"
|
|
2947
|
+
);
|
|
2907
2948
|
if (!bundle.engineSha256 && (bundle.schemaVersion === 1 || bundle.mode !== "record"))
|
|
2908
2949
|
fail(["engineSha256"], "A trial must pin the recorded engine bytes");
|
|
2909
2950
|
if (!bundle.catalog.targets.length)
|
|
@@ -3095,6 +3136,9 @@ var cliTrialChangeSchema = z28.object({
|
|
|
3095
3136
|
shippingIntent: cliInterventionSchema.shape.shippingIntent.unwrap()
|
|
3096
3137
|
}).strict();
|
|
3097
3138
|
|
|
3139
|
+
// ../packages/api-schemas/src/climb.ts
|
|
3140
|
+
import { z as z30 } from "zod";
|
|
3141
|
+
|
|
3098
3142
|
// ../packages/api-schemas/src/climb-kinds.ts
|
|
3099
3143
|
var LEVER_KINDS = [
|
|
3100
3144
|
"DOCS",
|
|
@@ -3536,6 +3580,7 @@ var diagnosisSchema = z31.object({
|
|
|
3536
3580
|
var trialRunSchema = z31.object({
|
|
3537
3581
|
id: z31.string(),
|
|
3538
3582
|
status: z31.string(),
|
|
3583
|
+
cliCoverage: cliCoverageSchema.nullable().optional(),
|
|
3539
3584
|
passed: z31.boolean().nullable(),
|
|
3540
3585
|
criteria: z31.array(
|
|
3541
3586
|
z31.object({ name: z31.string(), passed: z31.boolean(), reasoning: z31.string() })
|
|
@@ -4013,11 +4058,18 @@ var runPlanSchema = z39.object({
|
|
|
4013
4058
|
resources: z39.array(runPlanResourceSchema).default([]),
|
|
4014
4059
|
/** Server-populated context retained across deferred preparation. */
|
|
4015
4060
|
launch: z39.object({
|
|
4016
|
-
origin: z39.
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4061
|
+
origin: z39.discriminatedUnion("initiator", [
|
|
4062
|
+
z39.object({
|
|
4063
|
+
initiator: z39.literal("API"),
|
|
4064
|
+
userId: z39.string().min(1),
|
|
4065
|
+
email: z39.string().nullable()
|
|
4066
|
+
}).strict(),
|
|
4067
|
+
z39.object({
|
|
4068
|
+
initiator: z39.literal("GITHUB"),
|
|
4069
|
+
userId: z39.null(),
|
|
4070
|
+
email: z39.null()
|
|
4071
|
+
}).strict()
|
|
4072
|
+
]),
|
|
4021
4073
|
funding: z39.enum(["ORG", "PLATFORM"]),
|
|
4022
4074
|
queueClass: z39.literal("INTERACTIVE")
|
|
4023
4075
|
}).strict().optional(),
|
|
@@ -4028,7 +4080,7 @@ var runPlanSchema = z39.object({
|
|
|
4028
4080
|
digest: z39.string().min(1),
|
|
4029
4081
|
/** File submissions are asserted by the caller until server-side
|
|
4030
4082
|
* committed-byte fetching is available. */
|
|
4031
|
-
verification: z39.
|
|
4083
|
+
verification: z39.enum(["SUBMITTED_UNVERIFIED", "GITHUB_VERIFIED"]).optional()
|
|
4032
4084
|
}).optional(),
|
|
4033
4085
|
liveDependencies: z39.array(
|
|
4034
4086
|
z39.object({
|
|
@@ -4158,7 +4210,7 @@ var runDefinitionSubmissionSchema = z40.object({
|
|
|
4158
4210
|
repoUrl: uncredentialedRepoUrlSchema,
|
|
4159
4211
|
commit: z40.string().regex(/^[a-f0-9]{40}$/i),
|
|
4160
4212
|
digest: z40.string().regex(/^sha256:[a-f0-9]{64}$/),
|
|
4161
|
-
verification: z40.
|
|
4213
|
+
verification: z40.enum(["SUBMITTED_UNVERIFIED", "GITHUB_VERIFIED"])
|
|
4162
4214
|
}).strict(),
|
|
4163
4215
|
resources: z40.array(preparedDirectoryResourceSchema).max(1).default([]),
|
|
4164
4216
|
cases: z40.array(runDefinitionCaseSchema).min(1)
|
|
@@ -4200,6 +4252,13 @@ var runDefinitionSubmissionSchema = z40.object({
|
|
|
4200
4252
|
});
|
|
4201
4253
|
}
|
|
4202
4254
|
});
|
|
4255
|
+
var runDefinitionPublicSubmissionSchema = runDefinitionSubmissionSchema.refine(
|
|
4256
|
+
(definition) => definition.source.verification === "SUBMITTED_UNVERIFIED",
|
|
4257
|
+
{
|
|
4258
|
+
path: ["source", "verification"],
|
|
4259
|
+
message: "Only the GitHub App may verify committed bytes"
|
|
4260
|
+
}
|
|
4261
|
+
);
|
|
4203
4262
|
var runDefinitionPlanResponseSchema = z40.object({
|
|
4204
4263
|
source: runDefinitionSubmissionSchema.shape.source,
|
|
4205
4264
|
measurementMode: z40.literal("AGENT_EXPERIENCE"),
|
|
@@ -6347,9 +6406,112 @@ function cadenceLabel(cadence) {
|
|
|
6347
6406
|
|
|
6348
6407
|
// src/lib/runDefinitionFiles.ts
|
|
6349
6408
|
import { execFileSync } from "child_process";
|
|
6350
|
-
import { createHash as createHash2 } from "crypto";
|
|
6351
6409
|
import { isAbsolute, relative, resolve, sep } from "path";
|
|
6410
|
+
|
|
6411
|
+
// ../packages/api-schemas/src/compileRunDefinition.ts
|
|
6412
|
+
import { createHash as createHash2 } from "crypto";
|
|
6352
6413
|
import { parseDocument } from "yaml";
|
|
6414
|
+
function utf8(bytes, path2) {
|
|
6415
|
+
try {
|
|
6416
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
6417
|
+
} catch {
|
|
6418
|
+
throw new Error(`${path2} is not UTF-8 text`);
|
|
6419
|
+
}
|
|
6420
|
+
}
|
|
6421
|
+
function markdownCase(path2, bytes) {
|
|
6422
|
+
const source = utf8(bytes, path2);
|
|
6423
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(source);
|
|
6424
|
+
if (!match)
|
|
6425
|
+
throw new Error(`${path2}: expected YAML frontmatter between --- lines`);
|
|
6426
|
+
const document = parseDocument(match[1], { uniqueKeys: true });
|
|
6427
|
+
if (document.errors.length)
|
|
6428
|
+
throw new Error(
|
|
6429
|
+
`${path2}: ${document.errors.map((error) => error.message).join("; ")}`
|
|
6430
|
+
);
|
|
6431
|
+
const parsed = runDefinitionFrontmatterSchema.safeParse(document.toJS());
|
|
6432
|
+
if (!parsed.success) throw new Error(`${path2}: ${parsed.error.message}`);
|
|
6433
|
+
const prompt = match[2].trim();
|
|
6434
|
+
if (!prompt) throw new Error(`${path2}: Markdown body is empty`);
|
|
6435
|
+
return { path: path2, prompt, ...parsed.data };
|
|
6436
|
+
}
|
|
6437
|
+
function compileRunDefinitionFromBlobs(input) {
|
|
6438
|
+
if (!uncredentialedRepoUrlSchema.safeParse(input.repoUrl).success)
|
|
6439
|
+
throw new Error("Git origin must not contain embedded credentials");
|
|
6440
|
+
if (!/^[a-f0-9]{40}$/i.test(input.commit))
|
|
6441
|
+
throw new Error("Expected a full Git commit SHA");
|
|
6442
|
+
if (input.casePaths.length === 0)
|
|
6443
|
+
throw new Error("At least one Markdown case is required");
|
|
6444
|
+
if (new Set(input.casePaths).size !== input.casePaths.length)
|
|
6445
|
+
throw new Error("Case paths must be unique");
|
|
6446
|
+
const configBytes = input.blobs.get("gauge.json");
|
|
6447
|
+
let config;
|
|
6448
|
+
if (configBytes) {
|
|
6449
|
+
try {
|
|
6450
|
+
config = runDefinitionFileConfigSchema.parse(
|
|
6451
|
+
JSON.parse(utf8(configBytes, "gauge.json"))
|
|
6452
|
+
);
|
|
6453
|
+
} catch (error) {
|
|
6454
|
+
throw new Error(
|
|
6455
|
+
`gauge.json: ${error instanceof Error ? error.message : error}`
|
|
6456
|
+
);
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
const conventionalPrepare = input.blobs.has(".gauge/prepare.sh");
|
|
6460
|
+
if (config?.product && conventionalPrepare)
|
|
6461
|
+
throw new Error(
|
|
6462
|
+
"gauge.json: product and .gauge/prepare.sh are alternatives"
|
|
6463
|
+
);
|
|
6464
|
+
const prepare = config?.prepare ?? (conventionalPrepare ? "bash .gauge/prepare.sh" : void 0);
|
|
6465
|
+
const preparation = config?.product ?? (prepare ? { run: prepare } : void 0);
|
|
6466
|
+
if (config?.install && !prepare)
|
|
6467
|
+
throw new Error("gauge.json: install requires prepare");
|
|
6468
|
+
if (preparation && !input.repoUrl.startsWith("https://"))
|
|
6469
|
+
throw new Error("Project preparation requires a public HTTPS Git origin");
|
|
6470
|
+
const cases = input.casePaths.map((path2) => {
|
|
6471
|
+
const bytes = input.blobs.get(path2);
|
|
6472
|
+
if (!path2.endsWith(".md") || !bytes)
|
|
6473
|
+
throw new Error(`Missing committed Markdown case: ${path2}`);
|
|
6474
|
+
return markdownCase(path2, bytes);
|
|
6475
|
+
});
|
|
6476
|
+
const hash = createHash2("sha256");
|
|
6477
|
+
hash.update("gauge-run-definition-v1\0");
|
|
6478
|
+
for (const [path2, bytes] of [...input.blobs.entries()].sort(
|
|
6479
|
+
([a], [b]) => a < b ? -1 : a > b ? 1 : 0
|
|
6480
|
+
)) {
|
|
6481
|
+
hash.update(path2);
|
|
6482
|
+
hash.update("\0");
|
|
6483
|
+
hash.update(String(bytes.length));
|
|
6484
|
+
hash.update("\0");
|
|
6485
|
+
hash.update(bytes);
|
|
6486
|
+
hash.update("\0");
|
|
6487
|
+
}
|
|
6488
|
+
const submission = runDefinitionSubmissionSchema.parse({
|
|
6489
|
+
version: 1,
|
|
6490
|
+
source: {
|
|
6491
|
+
repoUrl: input.repoUrl,
|
|
6492
|
+
commit: input.commit,
|
|
6493
|
+
digest: `sha256:${hash.digest("hex")}`,
|
|
6494
|
+
verification: input.verification
|
|
6495
|
+
},
|
|
6496
|
+
resources: preparation ? [
|
|
6497
|
+
{
|
|
6498
|
+
name: "project",
|
|
6499
|
+
type: "prepared-directory",
|
|
6500
|
+
source: {
|
|
6501
|
+
type: "git-checkout",
|
|
6502
|
+
repoUrl: input.repoUrl,
|
|
6503
|
+
commit: input.commit
|
|
6504
|
+
},
|
|
6505
|
+
prepare: preparation,
|
|
6506
|
+
...config?.install ? { install: config.install } : {}
|
|
6507
|
+
}
|
|
6508
|
+
] : [],
|
|
6509
|
+
cases
|
|
6510
|
+
});
|
|
6511
|
+
return { definition: submission, org: config?.org, github: config?.github };
|
|
6512
|
+
}
|
|
6513
|
+
|
|
6514
|
+
// src/lib/runDefinitionFiles.ts
|
|
6353
6515
|
function git(cwd, args) {
|
|
6354
6516
|
try {
|
|
6355
6517
|
return execFileSync("git", args, {
|
|
@@ -6366,39 +6528,6 @@ function git(cwd, args) {
|
|
|
6366
6528
|
function committedBlob(root, commit, path2) {
|
|
6367
6529
|
return git(root, ["show", `${commit}:${path2}`]);
|
|
6368
6530
|
}
|
|
6369
|
-
function decode(blob, path2) {
|
|
6370
|
-
try {
|
|
6371
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(blob);
|
|
6372
|
-
} catch {
|
|
6373
|
-
throw new CliError("usage", `${path2} is not UTF-8 text`);
|
|
6374
|
-
}
|
|
6375
|
-
}
|
|
6376
|
-
function parseYaml(text, path2) {
|
|
6377
|
-
const document = parseDocument(text, { uniqueKeys: true });
|
|
6378
|
-
if (document.errors.length) {
|
|
6379
|
-
throw new CliError(
|
|
6380
|
-
"usage",
|
|
6381
|
-
`${path2}: ${document.errors.map((error) => error.message).join("; ")}`
|
|
6382
|
-
);
|
|
6383
|
-
}
|
|
6384
|
-
return document.toJS();
|
|
6385
|
-
}
|
|
6386
|
-
function caseFromMarkdown(path2, source) {
|
|
6387
|
-
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)([\s\S]*)$/.exec(source);
|
|
6388
|
-
if (!match)
|
|
6389
|
-
throw new CliError(
|
|
6390
|
-
"usage",
|
|
6391
|
-
`${path2}: expected YAML frontmatter between --- lines`
|
|
6392
|
-
);
|
|
6393
|
-
const parsed = runDefinitionFrontmatterSchema.safeParse(
|
|
6394
|
-
parseYaml(match[1], path2)
|
|
6395
|
-
);
|
|
6396
|
-
if (!parsed.success)
|
|
6397
|
-
throw new CliError("usage", `${path2}: ${parsed.error.message}`);
|
|
6398
|
-
const prompt = match[2].trim();
|
|
6399
|
-
if (!prompt) throw new CliError("usage", `${path2}: Markdown body is empty`);
|
|
6400
|
-
return { path: path2, prompt, ...parsed.data };
|
|
6401
|
-
}
|
|
6402
6531
|
function relativeGitPath(root, cwd, input) {
|
|
6403
6532
|
if (!input || input.includes("\0") || /[*?[\]{}]/.test(input))
|
|
6404
6533
|
throw new CliError("usage", `expected an explicit case path: ${input}`);
|
|
@@ -6436,83 +6565,34 @@ function loadCommittedRunDefinitionWithContext(files, cwd = process.cwd()) {
|
|
|
6436
6565
|
const paths = files.map((file) => relativeGitPath(root, cwd, file));
|
|
6437
6566
|
if (new Set(paths).size !== paths.length)
|
|
6438
6567
|
throw new CliError("usage", "case paths must be unique");
|
|
6439
|
-
const blobs =
|
|
6440
|
-
let config;
|
|
6568
|
+
const blobs = /* @__PURE__ */ new Map();
|
|
6441
6569
|
const hasConfig = git(root, ["ls-tree", "--name-only", commit, "--", "gauge.json"]).toString("utf8").trim() === "gauge.json";
|
|
6442
6570
|
if (hasConfig) {
|
|
6443
|
-
|
|
6444
|
-
const bytes = committedBlob(root, commit, "gauge.json");
|
|
6445
|
-
blobs.push({ path: "gauge.json", bytes });
|
|
6446
|
-
config = runDefinitionFileConfigSchema.parse(
|
|
6447
|
-
JSON.parse(decode(bytes, "gauge.json"))
|
|
6448
|
-
);
|
|
6449
|
-
} catch (error) {
|
|
6450
|
-
throw new CliError(
|
|
6451
|
-
"usage",
|
|
6452
|
-
`gauge.json: ${error instanceof Error ? error.message : error}`
|
|
6453
|
-
);
|
|
6454
|
-
}
|
|
6571
|
+
blobs.set("gauge.json", committedBlob(root, commit, "gauge.json"));
|
|
6455
6572
|
}
|
|
6456
6573
|
const hasConventionalPrepare = git(root, ["ls-tree", "--name-only", commit, "--", ".gauge/prepare.sh"]).toString("utf8").trim() === ".gauge/prepare.sh";
|
|
6457
6574
|
if (hasConventionalPrepare) {
|
|
6458
|
-
blobs.
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
});
|
|
6462
|
-
}
|
|
6463
|
-
if (config?.product && hasConventionalPrepare)
|
|
6464
|
-
throw new CliError(
|
|
6465
|
-
"usage",
|
|
6466
|
-
"gauge.json: product and .gauge/prepare.sh are alternatives"
|
|
6575
|
+
blobs.set(
|
|
6576
|
+
".gauge/prepare.sh",
|
|
6577
|
+
committedBlob(root, commit, ".gauge/prepare.sh")
|
|
6467
6578
|
);
|
|
6468
|
-
const prepare = config?.prepare ?? (hasConventionalPrepare ? "bash .gauge/prepare.sh" : void 0);
|
|
6469
|
-
const preparation = config?.product ?? (prepare ? { run: prepare } : void 0);
|
|
6470
|
-
if (config?.install && !prepare)
|
|
6471
|
-
throw new CliError("usage", "gauge.json: install requires prepare");
|
|
6472
|
-
if (preparation && !repoUrl.startsWith("https://"))
|
|
6473
|
-
throw new CliError(
|
|
6474
|
-
"usage",
|
|
6475
|
-
"Project preparation requires a public HTTPS Git origin"
|
|
6476
|
-
);
|
|
6477
|
-
const cases = paths.map((path2) => {
|
|
6478
|
-
const bytes = committedBlob(root, commit, path2);
|
|
6479
|
-
blobs.push({ path: path2, bytes });
|
|
6480
|
-
return caseFromMarkdown(path2, decode(bytes, path2));
|
|
6481
|
-
});
|
|
6482
|
-
const hash = createHash2("sha256");
|
|
6483
|
-
hash.update("gauge-run-definition-v1\0");
|
|
6484
|
-
for (const entry of blobs.sort(
|
|
6485
|
-
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
6486
|
-
)) {
|
|
6487
|
-
hash.update(entry.path);
|
|
6488
|
-
hash.update("\0");
|
|
6489
|
-
hash.update(String(entry.bytes.length));
|
|
6490
|
-
hash.update("\0");
|
|
6491
|
-
hash.update(entry.bytes);
|
|
6492
|
-
hash.update("\0");
|
|
6493
6579
|
}
|
|
6494
|
-
const
|
|
6495
|
-
|
|
6496
|
-
|
|
6580
|
+
for (const path2 of paths) blobs.set(path2, committedBlob(root, commit, path2));
|
|
6581
|
+
try {
|
|
6582
|
+
const result = compileRunDefinitionFromBlobs({
|
|
6497
6583
|
repoUrl,
|
|
6498
6584
|
commit,
|
|
6499
|
-
|
|
6585
|
+
casePaths: paths,
|
|
6586
|
+
blobs,
|
|
6500
6587
|
verification: "SUBMITTED_UNVERIFIED"
|
|
6501
|
-
}
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
}
|
|
6510
|
-
] : [],
|
|
6511
|
-
cases
|
|
6512
|
-
});
|
|
6513
|
-
if (!submission.success)
|
|
6514
|
-
throw new CliError("usage", submission.error.message);
|
|
6515
|
-
return { definition: submission.data, org: config?.org };
|
|
6588
|
+
});
|
|
6589
|
+
return { definition: result.definition, org: result.org };
|
|
6590
|
+
} catch (error) {
|
|
6591
|
+
throw new CliError(
|
|
6592
|
+
"usage",
|
|
6593
|
+
error instanceof Error ? error.message : String(error)
|
|
6594
|
+
);
|
|
6595
|
+
}
|
|
6516
6596
|
}
|
|
6517
6597
|
|
|
6518
6598
|
// src/lib/staff.ts
|
|
@@ -8269,6 +8349,18 @@ function printChange(content) {
|
|
|
8269
8349
|
console.log(doc.after || "(empty)");
|
|
8270
8350
|
}
|
|
8271
8351
|
}
|
|
8352
|
+
function printRoundPreview(preview) {
|
|
8353
|
+
for (const trial of preview.trials) {
|
|
8354
|
+
console.log(trial.title);
|
|
8355
|
+
for (const change of trial.changes ?? []) {
|
|
8356
|
+
console.log(`=== ${change.path}`);
|
|
8357
|
+
console.log(change.hunks);
|
|
8358
|
+
}
|
|
8359
|
+
for (const path2 of trial.droppedFiles ?? [])
|
|
8360
|
+
console.log(` ${path2}: dropped`);
|
|
8361
|
+
}
|
|
8362
|
+
console.log(`previewDigest ${preview.previewDigest}`);
|
|
8363
|
+
}
|
|
8272
8364
|
function printEstimate(e) {
|
|
8273
8365
|
console.log(
|
|
8274
8366
|
`plan: ${e.sampleCount} session${e.sampleCount === 1 ? "" : "s"} per trial \xD7 up to ${e.maxRounds} rounds`
|
|
@@ -8644,7 +8736,7 @@ function register19(program2) {
|
|
|
8644
8736
|
let last = "";
|
|
8645
8737
|
for (; ; ) {
|
|
8646
8738
|
const detail = await client.get(itemPath2(org, id));
|
|
8647
|
-
const trials2 = detail.rounds.flatMap((
|
|
8739
|
+
const trials2 = detail.rounds.flatMap((round2) => round2.trials);
|
|
8648
8740
|
const line = `${detail.status}${detail.stopReason ? ` (${detail.stopReason})` : ""} \xB7 round ${detail.round}/${detail.maxRounds} \xB7 ${pct(detail.before)} \u2192 ${pct(detail.after)} \xB7 ${detail.creditsSpent} credits spent \xB7 ${trials2.filter((t) => t.status === "RUNNING").map((t) => t.key).join(",") || "idle"}`;
|
|
8649
8741
|
if (line !== last) {
|
|
8650
8742
|
last = line;
|
|
@@ -8661,6 +8753,60 @@ function register19(program2) {
|
|
|
8661
8753
|
}
|
|
8662
8754
|
}
|
|
8663
8755
|
);
|
|
8756
|
+
group.command("authoring <id>").description(
|
|
8757
|
+
"What the next round may change: kinds, CLI targets and commands, baseline invocations, revision"
|
|
8758
|
+
).action(async (id, _opts, cmd) => {
|
|
8759
|
+
printJson(
|
|
8760
|
+
await createClient().get(`${itemPath2(resolveOrg(cmd), id)}/authoring`)
|
|
8761
|
+
);
|
|
8762
|
+
});
|
|
8763
|
+
group.command("invocation <id> <invocationId>").description(
|
|
8764
|
+
"One baseline CLI invocation from `authoring`, with its recorded command evidence"
|
|
8765
|
+
).action(
|
|
8766
|
+
async (id, invocationId, _opts, cmd) => {
|
|
8767
|
+
printJson(
|
|
8768
|
+
await createClient().get(
|
|
8769
|
+
`${itemPath2(resolveOrg(cmd), id)}/authoring/target`,
|
|
8770
|
+
{ cliInvocationId: invocationId }
|
|
8771
|
+
)
|
|
8772
|
+
);
|
|
8773
|
+
}
|
|
8774
|
+
);
|
|
8775
|
+
group.command("cli-change <id>").description(
|
|
8776
|
+
"Build a CLI_INTERVENTION bundle from {targetId, commandId, selector, operations, shippingIntent}; use the output as intervention.after in a round file. Saves nothing"
|
|
8777
|
+
).requiredOption("--file <path>", "the change JSON; '-' for stdin").action(async (id, opts, cmd) => {
|
|
8778
|
+
printJson(
|
|
8779
|
+
await createClient().post(
|
|
8780
|
+
`${itemPath2(resolveOrg(cmd), id)}/cli-changes`,
|
|
8781
|
+
readObject(opts.file)
|
|
8782
|
+
)
|
|
8783
|
+
);
|
|
8784
|
+
});
|
|
8785
|
+
const round = group.command("round").description(
|
|
8786
|
+
"Preview and save the round waiting to launch. A round file is {revision, trials: [{trial: {title, hypothesis, lever, intervention}, files?}]}; revision comes from `authoring`"
|
|
8787
|
+
);
|
|
8788
|
+
round.command("preview <id>").description("Validate a round file and print its diff and previewDigest").requiredOption("--file <path>", "the round JSON; '-' for stdin").action(async (id, opts, cmd) => {
|
|
8789
|
+
const preview = await createClient().post(
|
|
8790
|
+
`${itemPath2(resolveOrg(cmd), id)}/rounds/current/preview`,
|
|
8791
|
+
readObject(opts.file)
|
|
8792
|
+
);
|
|
8793
|
+
if (resolveOutput(cmd) === "json") printJson(preview);
|
|
8794
|
+
else printRoundPreview(preview);
|
|
8795
|
+
});
|
|
8796
|
+
round.command("save <id>").description(
|
|
8797
|
+
"Save a previewed round as drafts, replacing every pending draft. Never launches"
|
|
8798
|
+
).requiredOption("--file <path>", "the round JSON you previewed").requiredOption("--digest <digest>", "previewDigest from `round preview`").action(
|
|
8799
|
+
async (id, opts, cmd) => {
|
|
8800
|
+
const result = await createClient().put(
|
|
8801
|
+
`${itemPath2(resolveOrg(cmd), id)}/rounds/current`,
|
|
8802
|
+
{ ...readObject(opts.file), previewDigest: opts.digest }
|
|
8803
|
+
);
|
|
8804
|
+
if (resolveOutput(cmd) === "json") printJson(result);
|
|
8805
|
+
else
|
|
8806
|
+
for (const trial of result.trials)
|
|
8807
|
+
console.log(`Saved ${trial.key} "${trial.title}"`);
|
|
8808
|
+
}
|
|
8809
|
+
);
|
|
8664
8810
|
const trials = group.command("trials").description("List, author, and run trials");
|
|
8665
8811
|
trials.command("list <id>").description("Every trial of an optimization with score and verdict").action(async (id, _opts, cmd) => {
|
|
8666
8812
|
const org = resolveOrg(cmd);
|
|
@@ -8732,6 +8878,16 @@ function register19(program2) {
|
|
|
8732
8878
|
);
|
|
8733
8879
|
}
|
|
8734
8880
|
);
|
|
8881
|
+
trials.command("remove <id> <trialId>").description("Drop one draft trial from the round waiting to launch").option("--yes", "skip the confirmation").action(
|
|
8882
|
+
async (id, trialId, opts, cmd) => {
|
|
8883
|
+
await confirmOrAbort(`Remove draft ${trialId} from ${id}?`, opts.yes);
|
|
8884
|
+
const detail = await createClient().delete(
|
|
8885
|
+
trialPath(resolveOrg(cmd), id, trialId)
|
|
8886
|
+
);
|
|
8887
|
+
if (resolveOutput(cmd) === "json") printJson(detail);
|
|
8888
|
+
else console.log(`Removed ${trialId}.`);
|
|
8889
|
+
}
|
|
8890
|
+
);
|
|
8735
8891
|
group.command("trial <id> <trialId>").description("One trial by id or key (B0, R1.1, \u2026) with its sessions").action(
|
|
8736
8892
|
async (id, trialId, _opts, cmd) => {
|
|
8737
8893
|
const org = resolveOrg(cmd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withgauge/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Gauge customer CLI — Agent Preference, evals, runs, and organization settings over the Gauge API",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Gauge <support@withgauge.com> (https://withgauge.com)",
|