@eigenpal/cli 0.20.43 → 0.20.45
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/CHANGELOG.md +12 -0
- package/dist/cli.js +160 -28
- package/dist/docs-bundle.json +27 -13
- package/dist/skill/SKILL.md +34 -1
- package/dist/skill/reference/cli/agents.md +15 -15
- package/dist/skill/reference/cli/workflow.md +15 -15
- package/dist/skill/reference/dataset-format.md +28 -3
- package/dist/skill/reference/step-types.md +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @eigenpal/cli
|
|
2
2
|
|
|
3
|
+
## 0.20.45
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0be0b92: The CLI now follows the chunked Files upload transport, so large on-prem file uploads work without browser-reachable object storage.
|
|
8
|
+
|
|
9
|
+
## 0.20.45
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- ded47bf: Clarify how to flag specific dataset review fields and attach field comments with current workflow values. Agent guidance now keeps expected outputs aligned with the full intended workflow output and leaves comparison rules to evaluators.
|
|
14
|
+
|
|
3
15
|
## 0.20.42
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/cli.js
CHANGED
|
@@ -15494,7 +15494,7 @@ var init_human_review = __esm(() => {
|
|
|
15494
15494
|
}).refine((value) => value.slice(1).split("/").every((segment) => segment === "*" || !segment.includes("*")), { message: "A wildcard must occupy an entire JSON Pointer segment" });
|
|
15495
15495
|
HumanReviewCategoricalConfidenceSchema = _enum(["low", "medium", "high"]);
|
|
15496
15496
|
HumanReviewNumericConfidenceSchema = number2().finite().min(0).max(1);
|
|
15497
|
-
HumanReviewMetadataFromSchema = _enum(["ai.extract"]).describe("Producer adapter. `ai.extract` derives field confidence from `_grounding
|
|
15497
|
+
HumanReviewMetadataFromSchema = _enum(["ai.extract"]).describe("Producer adapter. `ai.extract` derives categorical field confidence from `_grounding`, copies valid numeric grounding scores into fieldMetadata.display.score, and strips that reserved key from review data. Grounding is not stripped unless this is set.");
|
|
15498
15498
|
HumanReviewResolvedConfidenceSchema = union([
|
|
15499
15499
|
HumanReviewNumericConfidenceSchema,
|
|
15500
15500
|
HumanReviewCategoricalConfidenceSchema,
|
|
@@ -15505,11 +15505,11 @@ var init_human_review = __esm(() => {
|
|
|
15505
15505
|
HumanReviewCategoricalConfidenceSchema
|
|
15506
15506
|
]);
|
|
15507
15507
|
HumanReviewFieldMetadataSchema = object({
|
|
15508
|
-
confidence: HumanReviewResolvedConfidenceSchema.optional().describe("
|
|
15508
|
+
confidence: HumanReviewResolvedConfidenceSchema.optional().describe("Routing confidence: categorical low|medium|high from ai.extract grounding, or legacy 0–1 numeric when authored. Numeric grounding scores from extract land in display.score instead. Not calibrated by Eigenpal."),
|
|
15509
15509
|
label: string2().max(500).optional().describe("Short label shown in the review UI"),
|
|
15510
15510
|
description: string2().max(5000).optional().describe("Longer reviewer guidance for this field"),
|
|
15511
15511
|
review: _enum(["auto", "always", "never"]).default("auto").optional().describe("Legacy per-field override kept for existing tasks. Prefer selection.fields.review."),
|
|
15512
|
-
display: record(string2(), unknown()).optional().describe("Opaque display metadata preserved for the review UI")
|
|
15512
|
+
display: record(string2(), unknown()).optional().describe("Opaque display metadata preserved for the review UI. When metadataFrom is ai.extract, numeric grounding scores are copied here as display.score; routing confidence stays on confidence as categorical low|medium|high.")
|
|
15513
15513
|
}).strict();
|
|
15514
15514
|
HumanReviewFieldPolicySchema = object({
|
|
15515
15515
|
review: _enum(["always", "never", "skip"]).optional().describe("Always require review, or never/skip review for matching paths"),
|
|
@@ -56616,6 +56616,9 @@ var init_eval = __esm(() => {
|
|
|
56616
56616
|
});
|
|
56617
56617
|
|
|
56618
56618
|
// ../types/src/git-source/grammar.ts
|
|
56619
|
+
function qualifyRunTargetName(name) {
|
|
56620
|
+
return /^wf_[A-Za-z0-9_-]+$/.test(name) ? `workflows.${name}` : name;
|
|
56621
|
+
}
|
|
56619
56622
|
function parseAutomationTarget(target) {
|
|
56620
56623
|
const parsed = AutomationTargetSchema.parse(target);
|
|
56621
56624
|
const [rawPackageName, rawRef] = parsed.split("@");
|
|
@@ -56634,7 +56637,7 @@ function parseRunTarget(target) {
|
|
|
56634
56637
|
const parsed = RunTargetSchema.parse(target);
|
|
56635
56638
|
if (typeof parsed === "string") {
|
|
56636
56639
|
const [name, rawRef] = parsed.split("@");
|
|
56637
|
-
const [root, ...segments] = name.split(".");
|
|
56640
|
+
const [root, ...segments] = qualifyRunTargetName(name).split(".");
|
|
56638
56641
|
const slug = segments.join("/");
|
|
56639
56642
|
if (root === "agents") {
|
|
56640
56643
|
return {
|
|
@@ -56855,11 +56858,11 @@ var init_grammar = __esm(() => {
|
|
|
56855
56858
|
ctx.addIssue({ code: "custom", message: "Run target must be <target> or <target>@<version>" });
|
|
56856
56859
|
return;
|
|
56857
56860
|
}
|
|
56858
|
-
const parts = name.split(".");
|
|
56861
|
+
const parts = qualifyRunTargetName(name).split(".");
|
|
56859
56862
|
if (parts.length < 2) {
|
|
56860
56863
|
ctx.addIssue({
|
|
56861
56864
|
code: "custom",
|
|
56862
|
-
message: "Run target must start with agents. or workflows. followed by a slug or id"
|
|
56865
|
+
message: "Run target must be a wf_ id or start with agents. or workflows. followed by a slug or id"
|
|
56863
56866
|
});
|
|
56864
56867
|
return;
|
|
56865
56868
|
}
|
|
@@ -207667,10 +207670,29 @@ function resolveRequestUrl(baseUrl, path) {
|
|
|
207667
207670
|
return `${base.origin}${requestPath}`;
|
|
207668
207671
|
return `${base.origin}${basePath}${requestPath}`;
|
|
207669
207672
|
}
|
|
207673
|
+
|
|
207674
|
+
// src/lib/fetch-body.ts
|
|
207675
|
+
function isNodeReadableStream(body) {
|
|
207676
|
+
return typeof body.pipe === "function";
|
|
207677
|
+
}
|
|
207678
|
+
function byteViewToArrayBuffer(view) {
|
|
207679
|
+
if (view.buffer instanceof ArrayBuffer && view.byteOffset === 0 && view.byteLength === view.buffer.byteLength) {
|
|
207680
|
+
return view.buffer;
|
|
207681
|
+
}
|
|
207682
|
+
const copy = new Uint8Array(view.byteLength);
|
|
207683
|
+
copy.set(view);
|
|
207684
|
+
return copy.buffer;
|
|
207685
|
+
}
|
|
207686
|
+
function toPutBody(body) {
|
|
207687
|
+
if (isNodeReadableStream(body)) {
|
|
207688
|
+
return body;
|
|
207689
|
+
}
|
|
207690
|
+
return byteViewToArrayBuffer(body);
|
|
207691
|
+
}
|
|
207670
207692
|
// package.json
|
|
207671
207693
|
var package_default = {
|
|
207672
207694
|
name: "@eigenpal/cli",
|
|
207673
|
-
version: "0.20.
|
|
207695
|
+
version: "0.20.45",
|
|
207674
207696
|
description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
|
|
207675
207697
|
keywords: [
|
|
207676
207698
|
"eigenpal",
|
|
@@ -207863,6 +207885,18 @@ class ApiClient {
|
|
|
207863
207885
|
});
|
|
207864
207886
|
return this.handleResponse(res, url);
|
|
207865
207887
|
}
|
|
207888
|
+
async putBytes(path, body) {
|
|
207889
|
+
const url = this.buildUrl(path);
|
|
207890
|
+
const stream = isNodeReadableStream(body);
|
|
207891
|
+
const init = {
|
|
207892
|
+
method: "PUT",
|
|
207893
|
+
headers: this.headers({ "Content-Type": "application/octet-stream" }),
|
|
207894
|
+
body,
|
|
207895
|
+
...stream ? { duplex: "half" } : {}
|
|
207896
|
+
};
|
|
207897
|
+
const res = await fetch(url, init);
|
|
207898
|
+
return this.handleResponse(res, url);
|
|
207899
|
+
}
|
|
207866
207900
|
async patch(path, body) {
|
|
207867
207901
|
const url = this.buildUrl(path);
|
|
207868
207902
|
const res = await fetch(url, {
|
|
@@ -211510,7 +211544,7 @@ function registerDatasetReviewRequestCommands(dataset, resolveAutomation) {
|
|
|
211510
211544
|
{ key: "createdAt", header: "createdAt", format: formatTimestamp }
|
|
211511
211545
|
], { ...opts, entityLabel: "review request" });
|
|
211512
211546
|
}));
|
|
211513
|
-
addJsonFlag(withBaseUrl(reviewRequest.command("create <automation-id>"))).description("Request human review of dataset ground truth. Snapshot examples and poll progress until review is complete.").requiredOption("--title <title>", "Review request title").requiredOption("--example-name <name>", "Example folder name to include (repeatable)", collectRepeatable, []).option("--instructions <text>", "Note shown to the reviewer for the whole request").option("--focus <path>", "Expected-output path to
|
|
211547
|
+
addJsonFlag(withBaseUrl(reviewRequest.command("create <automation-id>"))).description("Request human review of dataset ground truth. Snapshot examples and poll progress until review is complete.").requiredOption("--title <title>", "Review request title").requiredOption("--example-name <name>", "Example folder name to include (repeatable)", collectRepeatable, []).option("--instructions <text>", "Note shown to the reviewer for the whole request").option("--focus <path>", "Expected-output leaf path to flag across selected examples; parent paths flag every descendant (repeatable)", collectRepeatable, []).option("--focus-reason <spec>", "Reason a focus path needs review, as path=reason (repeatable)", collectRepeatable, []).option("--ignore <path>", "Expected-output path reviewers can skip (repeatable)", collectRepeatable, []).option("--item-note <spec>", "Example-level note seeded at create time, as exampleName=comment (repeatable)", collectRepeatable, []).option("--field-note <spec>", "Field comment only, as exampleName.path=comment; does not flag the field (repeatable)", collectRepeatable, []).option("--focus-json <json>", "JSON array of { path, reason? } focus fields").option("--notes-json <json>", "JSON array of { exampleName, comment?, fields?: [{ path, comment }] }").option("--status <draft|open|paused>", "Initial lifecycle status (default: draft)").addHelpText("after", `
|
|
211514
211548
|
Examples:
|
|
211515
211549
|
$ eigenpal workflow dataset review-request create wf_abc123 \\
|
|
211516
211550
|
--title "Q1 invoice GT review" \\
|
|
@@ -211518,12 +211552,21 @@ Examples:
|
|
|
211518
211552
|
--instructions "Check IBAN checksums and totals." \\
|
|
211519
211553
|
--focus vendor.iban --focus-reason 'vendor.iban=OCR often mangles IBANs' \\
|
|
211520
211554
|
--ignore currency \\
|
|
211521
|
-
--field-note 'invoice-foo.
|
|
211555
|
+
--field-note 'invoice-foo.vendor.iban=Workflow currently returns SK12; expected SK34. Verify against the document.' \\
|
|
211522
211556
|
--status open --json
|
|
211523
211557
|
|
|
211524
211558
|
$ eigenpal workflow dataset review-request list wf_abc123 --status open --json
|
|
211525
211559
|
$ eigenpal workflow dataset review-request get wf_abc123 dsr_... --json
|
|
211526
211560
|
|
|
211561
|
+
Keep expected.json one-to-one with the complete intended workflow output,
|
|
211562
|
+
including nested objects, arrays, types, and expected file references. Evaluators
|
|
211563
|
+
define which values to compare and how; never reshape ground truth for scoring.
|
|
211564
|
+
Review snapshots must keep that full dataset shape. Flag only uncertain or
|
|
211565
|
+
incorrect leaf paths (for example items.3.amount), not every field or a whole
|
|
211566
|
+
array unless every descendant needs review. Focus applies to all selected
|
|
211567
|
+
examples. A field note adds context without flagging: include the workflow's
|
|
211568
|
+
current value, the expected value, and what the reviewer should verify.
|
|
211569
|
+
|
|
211527
211570
|
Agent loop: create with focus/ignore and per-example notes → poll \`get --json\`
|
|
211528
211571
|
until \`.progress.complete\` → record per-field decisions and per-example
|
|
211529
211572
|
approve/remove with comments → \`pull --out <dir>\` to fetch expected files
|
|
@@ -244453,25 +244496,6 @@ import { createReadStream } from "node:fs";
|
|
|
244453
244496
|
import { stat as stat3 } from "node:fs/promises";
|
|
244454
244497
|
import path17 from "node:path";
|
|
244455
244498
|
|
|
244456
|
-
// src/lib/fetch-body.ts
|
|
244457
|
-
function isNodeReadableStream(body) {
|
|
244458
|
-
return typeof body.pipe === "function";
|
|
244459
|
-
}
|
|
244460
|
-
function byteViewToArrayBuffer(view) {
|
|
244461
|
-
if (view.buffer instanceof ArrayBuffer && view.byteOffset === 0 && view.byteLength === view.buffer.byteLength) {
|
|
244462
|
-
return view.buffer;
|
|
244463
|
-
}
|
|
244464
|
-
const copy = new Uint8Array(view.byteLength);
|
|
244465
|
-
copy.set(view);
|
|
244466
|
-
return copy.buffer;
|
|
244467
|
-
}
|
|
244468
|
-
function toPutBody(body) {
|
|
244469
|
-
if (isNodeReadableStream(body)) {
|
|
244470
|
-
return body;
|
|
244471
|
-
}
|
|
244472
|
-
return byteViewToArrayBuffer(body);
|
|
244473
|
-
}
|
|
244474
|
-
|
|
244475
244499
|
// src/lib/upload-presigned-multipart.ts
|
|
244476
244500
|
var MULTIPART_UPLOAD_CONCURRENCY = 4;
|
|
244477
244501
|
var MULTIPART_PART_MAX_RETRIES = 4;
|
|
@@ -244645,6 +244669,79 @@ async function uploadPresignedMultipartParts(options) {
|
|
|
244645
244669
|
}
|
|
244646
244670
|
}
|
|
244647
244671
|
|
|
244672
|
+
// src/lib/upload-app-chunked.ts
|
|
244673
|
+
var HTTP_CHUNKED_MAX_RETRIES = 4;
|
|
244674
|
+
var HTTP_CHUNKED_RETRY_BASE_MS = 250;
|
|
244675
|
+
function expectedHttpChunkByteLength(totalSize, chunkSizeBytes, chunkIndex, chunkCount) {
|
|
244676
|
+
if (!Number.isInteger(chunkIndex) || chunkIndex < 0 || chunkIndex >= chunkCount) {
|
|
244677
|
+
throw new Error(`chunkIndex must be an integer in [0, ${chunkCount})`);
|
|
244678
|
+
}
|
|
244679
|
+
if (totalSize === 0)
|
|
244680
|
+
return 0;
|
|
244681
|
+
if (chunkIndex < chunkCount - 1)
|
|
244682
|
+
return chunkSizeBytes;
|
|
244683
|
+
const remainder = totalSize % chunkSizeBytes;
|
|
244684
|
+
return remainder === 0 ? chunkSizeBytes : remainder;
|
|
244685
|
+
}
|
|
244686
|
+
function chunkByteOffset(chunkSizeBytes, chunkIndex) {
|
|
244687
|
+
return chunkIndex * chunkSizeBytes;
|
|
244688
|
+
}
|
|
244689
|
+
function shouldAbortHttpChunkedUploadSession(options) {
|
|
244690
|
+
return !options.chunksReady;
|
|
244691
|
+
}
|
|
244692
|
+
function httpChunkedCompleteRetryHint(uploadId) {
|
|
244693
|
+
return `Uploaded chunks remain stored for ${uploadId}; retry complete and do not abort the session.`;
|
|
244694
|
+
}
|
|
244695
|
+
function annotateHttpChunkedCompleteFailure(uploadId, error) {
|
|
244696
|
+
const hint = httpChunkedCompleteRetryHint(uploadId);
|
|
244697
|
+
if (error instanceof Error) {
|
|
244698
|
+
if (!error.message.includes("retry complete")) {
|
|
244699
|
+
error.message = `${error.message} ${hint}`;
|
|
244700
|
+
}
|
|
244701
|
+
return error;
|
|
244702
|
+
}
|
|
244703
|
+
return new Error(`${String(error)} ${hint}`);
|
|
244704
|
+
}
|
|
244705
|
+
|
|
244706
|
+
class ChunkUploadHttpError extends Error {
|
|
244707
|
+
status;
|
|
244708
|
+
constructor(status) {
|
|
244709
|
+
super(`Chunk upload failed (${status})`);
|
|
244710
|
+
this.status = status;
|
|
244711
|
+
this.name = "ChunkUploadHttpError";
|
|
244712
|
+
}
|
|
244713
|
+
}
|
|
244714
|
+
function isRetryableChunkError(error) {
|
|
244715
|
+
if (error instanceof ChunkUploadHttpError)
|
|
244716
|
+
return isTransientHttpStatus(error.status);
|
|
244717
|
+
return true;
|
|
244718
|
+
}
|
|
244719
|
+
async function uploadHttpChunks(options) {
|
|
244720
|
+
for (let chunkIndex = 0;chunkIndex < options.chunkCount; chunkIndex++) {
|
|
244721
|
+
const length = expectedHttpChunkByteLength(options.totalSize, options.chunkSizeBytes, chunkIndex, options.chunkCount);
|
|
244722
|
+
const start = chunkByteOffset(options.chunkSizeBytes, chunkIndex);
|
|
244723
|
+
let lastError;
|
|
244724
|
+
for (let attempt = 0;attempt <= HTTP_CHUNKED_MAX_RETRIES; attempt++) {
|
|
244725
|
+
try {
|
|
244726
|
+
await options.putChunk({ chunkIndex, start, length });
|
|
244727
|
+
lastError = undefined;
|
|
244728
|
+
break;
|
|
244729
|
+
} catch (error) {
|
|
244730
|
+
lastError = error;
|
|
244731
|
+
if (!isRetryableChunkError(error) || attempt === HTTP_CHUNKED_MAX_RETRIES) {
|
|
244732
|
+
throw error;
|
|
244733
|
+
}
|
|
244734
|
+
await sleep2(HTTP_CHUNKED_RETRY_BASE_MS * 2 ** attempt);
|
|
244735
|
+
}
|
|
244736
|
+
}
|
|
244737
|
+
if (lastError)
|
|
244738
|
+
throw lastError;
|
|
244739
|
+
}
|
|
244740
|
+
}
|
|
244741
|
+
function sleep2(ms) {
|
|
244742
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
244743
|
+
}
|
|
244744
|
+
|
|
244648
244745
|
// src/lib/upload-reusable-file.ts
|
|
244649
244746
|
function newIdempotencyKey() {
|
|
244650
244747
|
return randomUUID();
|
|
@@ -244673,6 +244770,9 @@ async function uploadReusableFile(client, input) {
|
|
|
244673
244770
|
if (negotiation.transport === "presigned-multipart") {
|
|
244674
244771
|
return uploadPresignedMultipart(client, source, negotiation);
|
|
244675
244772
|
}
|
|
244773
|
+
if (negotiation.transport === "http-chunked") {
|
|
244774
|
+
return uploadHttpChunked(client, source, negotiation);
|
|
244775
|
+
}
|
|
244676
244776
|
let putReady = false;
|
|
244677
244777
|
try {
|
|
244678
244778
|
const putBody = source.kind === "buffer" ? toPutBody(new Uint8Array(source.content)) : toPutBody(createReadStream(source.filePath));
|
|
@@ -244724,6 +244824,38 @@ async function resolveUploadSource(input) {
|
|
|
244724
244824
|
contentType
|
|
244725
244825
|
};
|
|
244726
244826
|
}
|
|
244827
|
+
async function uploadHttpChunked(client, source, session) {
|
|
244828
|
+
const chunksPath = apiPath(session.chunksUrl);
|
|
244829
|
+
const completePath = apiPath(session.completeUrl);
|
|
244830
|
+
let chunksReady = false;
|
|
244831
|
+
try {
|
|
244832
|
+
await uploadHttpChunks({
|
|
244833
|
+
chunkCount: session.chunkCount,
|
|
244834
|
+
chunkSizeBytes: session.chunkSizeBytes,
|
|
244835
|
+
totalSize: source.size,
|
|
244836
|
+
putChunk: async ({ chunkIndex, start, length }) => {
|
|
244837
|
+
const body = source.kind === "buffer" ? toPutBody(source.content.subarray(start, start + length)) : length === 0 ? toPutBody(new Uint8Array) : toPutBody(createReadStream(source.filePath, { start, end: start + length - 1 }));
|
|
244838
|
+
try {
|
|
244839
|
+
await client.putBytes(`${chunksPath}/${chunkIndex}`, body);
|
|
244840
|
+
} catch (error) {
|
|
244841
|
+
if (error instanceof ApiError)
|
|
244842
|
+
throw new ChunkUploadHttpError(error.status);
|
|
244843
|
+
throw error;
|
|
244844
|
+
}
|
|
244845
|
+
}
|
|
244846
|
+
});
|
|
244847
|
+
chunksReady = true;
|
|
244848
|
+
return await client.post(completePath, {});
|
|
244849
|
+
} catch (error) {
|
|
244850
|
+
if (shouldAbortHttpChunkedUploadSession({ chunksReady })) {
|
|
244851
|
+
await client.delete(apiPath(`/files/uploads/${session.uploadId}`)).catch(() => {
|
|
244852
|
+
return;
|
|
244853
|
+
});
|
|
244854
|
+
throw error;
|
|
244855
|
+
}
|
|
244856
|
+
throw annotateHttpChunkedCompleteFailure(session.uploadId, error);
|
|
244857
|
+
}
|
|
244858
|
+
}
|
|
244727
244859
|
async function uploadPresignedMultipart(client, source, session) {
|
|
244728
244860
|
const partsPath = apiPath(session.partsUrl);
|
|
244729
244861
|
const completePath = apiPath(session.completeUrl);
|