orizu 0.5.27 → 0.5.29
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 +52 -0
- package/dist/cli-project-slug.d.ts +17 -0
- package/dist/cli-project-slug.d.ts.map +1 -0
- package/dist/cli-project-slug.js +24 -0
- package/dist/credentials.d.ts +4 -0
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +15 -4
- package/dist/dataset-transfer-download.d.ts +2 -0
- package/dist/dataset-transfer-download.d.ts.map +1 -0
- package/dist/dataset-transfer-download.js +66 -0
- package/dist/dataset-transfer-policy.d.ts +2 -0
- package/dist/dataset-transfer-policy.d.ts.map +1 -0
- package/dist/dataset-transfer-policy.js +8 -0
- package/dist/dataset-transfer-upload.d.ts +42 -0
- package/dist/dataset-transfer-upload.d.ts.map +1 -0
- package/dist/dataset-transfer-upload.js +110 -0
- package/dist/feedback-cli.d.ts +14 -0
- package/dist/feedback-cli.d.ts.map +1 -0
- package/dist/feedback-cli.js +601 -0
- package/dist/feedback-contract.d.ts +77 -0
- package/dist/feedback-contract.d.ts.map +1 -0
- package/dist/feedback-contract.js +414 -0
- package/dist/feedback-scrub.d.ts +16 -0
- package/dist/feedback-scrub.d.ts.map +1 -0
- package/dist/feedback-scrub.js +324 -0
- package/dist/headless-login.d.ts +2 -0
- package/dist/headless-login.d.ts.map +1 -1
- package/dist/headless-login.js +14 -11
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +35 -1
- package/dist/hosted-boot.d.ts +5 -0
- package/dist/hosted-boot.d.ts.map +1 -1
- package/dist/hosted-boot.js +12 -3
- package/dist/hosted-harness-opencode.d.ts +8 -9
- package/dist/hosted-harness-opencode.d.ts.map +1 -1
- package/dist/hosted-harness-opencode.js +12 -9
- package/dist/hosted-loop.d.ts.map +1 -1
- package/dist/hosted-loop.js +15 -9
- package/dist/http.d.ts +6 -4
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +51 -28
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -111
- package/dist/instruction-set-sync/index.d.ts +1 -0
- package/dist/instruction-set-sync/index.d.ts.map +1 -1
- package/dist/instruction-set-sync/index.js +15 -6
- package/dist/last-error-caps.d.ts +6 -0
- package/dist/last-error-caps.d.ts.map +1 -0
- package/dist/last-error-caps.js +5 -0
- package/dist/last-error-record.d.ts +34 -0
- package/dist/last-error-record.d.ts.map +1 -0
- package/dist/last-error-record.js +289 -0
- package/dist/runner-env.d.ts +3 -0
- package/dist/runner-env.d.ts.map +1 -0
- package/dist/runner-env.js +29 -0
- package/dist/self-update.d.ts +42 -0
- package/dist/self-update.d.ts.map +1 -0
- package/dist/self-update.js +506 -0
- package/package.json +2 -1
- package/src/cli-project-slug.ts +38 -0
- package/src/credentials.ts +21 -4
- package/src/dataset-transfer-download.ts +61 -0
- package/src/dataset-transfer-policy.ts +8 -0
- package/src/dataset-transfer-upload.ts +145 -0
- package/src/feedback-cli.ts +763 -0
- package/src/feedback-contract.ts +473 -0
- package/src/feedback-scrub.ts +466 -0
- package/src/headless-login.ts +25 -11
- package/src/help.ts +35 -1
- package/src/hosted-boot.ts +21 -3
- package/src/hosted-harness-opencode.ts +12 -9
- package/src/hosted-loop.ts +15 -8
- package/src/http.ts +56 -27
- package/src/index.ts +82 -152
- package/src/instruction-set-sync/index.ts +25 -6
- package/src/last-error-caps.js +4 -0
- package/src/last-error-caps.ts +5 -0
- package/src/last-error-record.ts +382 -0
- package/src/runner-env.ts +31 -0
- package/src/secret-redaction.js +4 -0
- package/src/self-update.ts +548 -0
- package/vendor/skills/orizu/SKILL.md +4 -0
- package/vendor/skills/orizu/flows/expand.md +2 -0
- package/vendor/skills/orizu/flows/first-win.md +2 -0
- package/vendor/skills/orizu/flows/onboard.md +2 -0
- package/vendor/skills/orizu/flows/promote.md +2 -0
- package/vendor/skills/orizu/flows/recurse.md +2 -0
- package/vendor/skills/orizu/flows/triage.md +2 -0
- package/vendor/skills/orizu/references/authority-map.md +1 -0
- package/vendor/skills/orizu/references/cli-reference.md +62 -0
- package/vendor/skills/orizu/references/feedback.md +101 -0
- package/vendor/skills/orizu/references/prompt-control-plane.md +4 -3
package/README.md
CHANGED
|
@@ -156,6 +156,35 @@ This prints the skill location plus source metadata (`name`, `root`, `skillMd`,
|
|
|
156
156
|
to reading `SKILL.md` in two commands and verify the guidance matches the CLI
|
|
157
157
|
that supplied it.
|
|
158
158
|
|
|
159
|
+
### Update the CLI
|
|
160
|
+
|
|
161
|
+
An npm-global install can update itself to npm's `latest` release:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
orizu update [--dry-run] [--json]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The command detects the npm prefix, checks and compares versions, installs only
|
|
168
|
+
when `latest` is newer, and verifies the newly installed binary in a fresh
|
|
169
|
+
process. A successful `orizu update` automatically runs `orizu skills update`
|
|
170
|
+
with the installed CLI, including when Orizu is already the latest. `--dry-run`
|
|
171
|
+
stops after the version comparison without installing or refreshing skills. Source checkouts,
|
|
172
|
+
`npx` caches, and pnpm, Bun, or Yarn global layouts are refused without being
|
|
173
|
+
modified.
|
|
174
|
+
For now, Windows installs and custom npm prefixes without their own `bin/node`
|
|
175
|
+
(for example `npm config set prefix ~/.npm-global`) are refused with
|
|
176
|
+
`update_unsupported_install`; use the manual npm command in that error.
|
|
177
|
+
|
|
178
|
+
On other human-readable commands, an npm-global install may print one stderr
|
|
179
|
+
line when its cached release check finds a newer version. Cache refresh happens
|
|
180
|
+
in a detached process at most once every 24 hours, so commands do not wait for
|
|
181
|
+
the registry. Notices and refreshes are suppressed for `--json`, CI,
|
|
182
|
+
`NO_UPDATE_NOTIFIER`, and `update` itself. Disable them explicitly with:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
ORIZU_NO_UPDATE_CHECK=1 orizu --help
|
|
186
|
+
```
|
|
187
|
+
|
|
159
188
|
Agents are first-class users of this CLI: **every command supports `--json`**
|
|
160
189
|
(as a prefix, `orizu --json teams list`, or a trailing flag) and emits a single
|
|
161
190
|
machine-readable JSON document instead of formatted text. Discover the full
|
|
@@ -210,6 +239,10 @@ orizu logout
|
|
|
210
239
|
<td>Locate the bundled skill (read-only)</td>
|
|
211
240
|
<td><code>orizu skills path --json</code></td>
|
|
212
241
|
</tr>
|
|
242
|
+
<tr>
|
|
243
|
+
<td>Update an npm-global CLI and its installed skills</td>
|
|
244
|
+
<td><code>orizu update</code></td>
|
|
245
|
+
</tr>
|
|
213
246
|
<tr>
|
|
214
247
|
<td>Inspect CLI capabilities as JSON</td>
|
|
215
248
|
<td><code>orizu capabilities --json</code></td>
|
|
@@ -427,6 +460,25 @@ Task and dataset export support:
|
|
|
427
460
|
- `json`
|
|
428
461
|
- `jsonl`
|
|
429
462
|
|
|
463
|
+
Dataset `upload` and `push` create an immutable version after the rows are
|
|
464
|
+
acknowledged. With `--json`, upload prints one result document. A failed snapshot
|
|
465
|
+
reports the uploaded dataset identity so recovery can target the snapshot step.
|
|
466
|
+
If a write's response is lost or times out, inspect the dataset before retrying:
|
|
467
|
+
the last chunk may already have committed.
|
|
468
|
+
|
|
469
|
+
Dataset downloads retry transient read failures up to three attempts. Each
|
|
470
|
+
transfer request, including its response body, has a two-minute deadline;
|
|
471
|
+
`ORIZU_DATASET_TRANSFER_TIMEOUT_MS` accepts 1–300000 milliseconds. Downloads
|
|
472
|
+
receive the complete response before writing to the destination. Existing files
|
|
473
|
+
keep their inode, ownership, permissions and ACLs; writes follow existing
|
|
474
|
+
symlinks and update hardlinked readers. New files request mode `0600`, subject
|
|
475
|
+
to the filesystem's access rules. A failed network transfer leaves an existing output
|
|
476
|
+
untouched. The final write retains ordinary filesystem behavior: it is not
|
|
477
|
+
atomic, and a disk failure during that write can leave partial output.
|
|
478
|
+
SIGINT/SIGTERM cancels a download. Upload writes
|
|
479
|
+
have no additional transient retries; the existing session refresh behavior is
|
|
480
|
+
unchanged.
|
|
481
|
+
|
|
430
482
|
## Interactive And Automated Usage
|
|
431
483
|
|
|
432
484
|
Many commands can prompt for missing team, project, app, dataset, or task selections in an interactive terminal. In scripts and CI, pass explicit flags instead:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface ParsedProjectSlug {
|
|
2
|
+
teamSlug: string;
|
|
3
|
+
projectSlug: string;
|
|
4
|
+
}
|
|
5
|
+
export interface SplitProjectArg {
|
|
6
|
+
/** null when the caller passed a bare project slug. */
|
|
7
|
+
teamSlug: string | null;
|
|
8
|
+
projectSlug: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function normalizeSlugInput(slug: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Accept a `--project` value in either documented form: `teamSlug/projectSlug`
|
|
13
|
+
* or a bare `projectSlug` (resolved within the caller's team).
|
|
14
|
+
*/
|
|
15
|
+
export declare function splitCliProjectArg(value: string): SplitProjectArg | null;
|
|
16
|
+
export declare function parseCliProjectSlug(projectSlug: string): ParsedProjectSlug | null;
|
|
17
|
+
//# sourceMappingURL=cli-project-slug.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-project-slug.d.ts","sourceRoot":"","sources":["../src/cli-project-slug.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAQxE;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAQjF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function normalizeSlugInput(slug) {
|
|
2
|
+
return slug.trim().toLowerCase();
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Accept a `--project` value in either documented form: `teamSlug/projectSlug`
|
|
6
|
+
* or a bare `projectSlug` (resolved within the caller's team).
|
|
7
|
+
*/
|
|
8
|
+
export function splitCliProjectArg(value) {
|
|
9
|
+
if (!value.includes('/')) {
|
|
10
|
+
const projectSlug = normalizeSlugInput(value);
|
|
11
|
+
return projectSlug ? { teamSlug: null, projectSlug } : null;
|
|
12
|
+
}
|
|
13
|
+
const parsed = parseCliProjectSlug(value);
|
|
14
|
+
return parsed ? { teamSlug: parsed.teamSlug, projectSlug: parsed.projectSlug } : null;
|
|
15
|
+
}
|
|
16
|
+
export function parseCliProjectSlug(projectSlug) {
|
|
17
|
+
const segments = projectSlug.split('/');
|
|
18
|
+
if (segments.length !== 2)
|
|
19
|
+
return null;
|
|
20
|
+
const [teamSlug, project] = segments.map(normalizeSlugInput);
|
|
21
|
+
if (!teamSlug || !project)
|
|
22
|
+
return null;
|
|
23
|
+
return { teamSlug, projectSlug: project };
|
|
24
|
+
}
|
package/dist/credentials.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ServerCredentials, StoredCredentialsV2, StoredCredentialsV3 } from './types.js';
|
|
2
|
+
export declare function getConfigDir(): string;
|
|
2
3
|
/**
|
|
3
4
|
* Runs a synchronous credentials transaction under the one process-wide and
|
|
4
5
|
* cross-process lock. Exported so real-process fixtures can hold the production
|
|
@@ -18,6 +19,9 @@ export declare function updateServerCredentials(baseUrl: string, credentials: Se
|
|
|
18
19
|
export declare function credentialsEqual(left: ServerCredentials, right: ServerCredentials): boolean;
|
|
19
20
|
export declare function updateServerCredentialsIfCurrent(baseUrl: string, expected: ServerCredentials, replacement: ServerCredentials): boolean;
|
|
20
21
|
export declare function getActiveBaseUrl(): string | null;
|
|
22
|
+
export declare function rememberProcessSecret(secret: string): string;
|
|
23
|
+
export declare function rememberResolvedBearer(bearer: string): string;
|
|
24
|
+
export declare function getSecretsSeenThisProcess(): readonly string[];
|
|
21
25
|
/**
|
|
22
26
|
* In-sandbox bearer resolution (ALI-1044). Resolves a bearer supplied out-of-band
|
|
23
27
|
* to the hosted agent, WITHOUT consulting credentials.json:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAiBA,OAAO,EACL,iBAAiB,EAEjB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAiBA,OAAO,EACL,iBAAiB,EAEjB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,YAAY,CAAA;AAMnB,wBAAgB,YAAY,IAAI,MAAM,CAKrC;AA0HD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAgCvE;AAED;;;;GAIG;AACH,wBAAsB,mCAAmC,CAAC,CAAC,EACzD,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAiDZ;AAwGD,wBAAgB,qBAAqB,IAAI,mBAAmB,GAAG,mBAAmB,GAAG,IAAI,CA8BxF;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAW9E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,QAepF;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,QAatF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAO3F;AAED,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,iBAAiB,EAC3B,WAAW,EAAE,iBAAiB,GAC7B,OAAO,CAUT;AAED,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAGhD;AAID,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,yBAAyB,IAAI,SAAS,MAAM,EAAE,CAE7D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CA0BrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAclE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAS1D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,QAMtD;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAWT;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAc/D;AAED,wBAAgB,oBAAoB,SAKnC"}
|
package/dist/credentials.js
CHANGED
|
@@ -6,7 +6,7 @@ import { AsyncLocalStorage } from 'async_hooks';
|
|
|
6
6
|
function isNodeError(error) {
|
|
7
7
|
return error instanceof Error;
|
|
8
8
|
}
|
|
9
|
-
function getConfigDir() {
|
|
9
|
+
export function getConfigDir() {
|
|
10
10
|
if (process.env.ORIZU_CONFIG_DIR) {
|
|
11
11
|
return process.env.ORIZU_CONFIG_DIR;
|
|
12
12
|
}
|
|
@@ -364,6 +364,17 @@ export function getActiveBaseUrl() {
|
|
|
364
364
|
const config = loadCredentialsConfig();
|
|
365
365
|
return config?.activeBaseUrl || null;
|
|
366
366
|
}
|
|
367
|
+
const secretsSeenThisProcess = new Set();
|
|
368
|
+
export function rememberProcessSecret(secret) {
|
|
369
|
+
secretsSeenThisProcess.add(secret);
|
|
370
|
+
return secret;
|
|
371
|
+
}
|
|
372
|
+
export function rememberResolvedBearer(bearer) {
|
|
373
|
+
return rememberProcessSecret(bearer);
|
|
374
|
+
}
|
|
375
|
+
export function getSecretsSeenThisProcess() {
|
|
376
|
+
return [...secretsSeenThisProcess];
|
|
377
|
+
}
|
|
367
378
|
/**
|
|
368
379
|
* In-sandbox bearer resolution (ALI-1044). Resolves a bearer supplied out-of-band
|
|
369
380
|
* to the hosted agent, WITHOUT consulting credentials.json:
|
|
@@ -385,7 +396,7 @@ export function getActiveBaseUrl() {
|
|
|
385
396
|
export function resolveEnvBearerToken() {
|
|
386
397
|
const explicit = process.env.ORIZU_TOKEN;
|
|
387
398
|
if (explicit && explicit.trim()) {
|
|
388
|
-
return explicit.trim();
|
|
399
|
+
return rememberResolvedBearer(explicit.trim());
|
|
389
400
|
}
|
|
390
401
|
const tokenFile = process.env.ORIZU_TOKEN_FILE;
|
|
391
402
|
if (tokenFile && tokenFile.length > 0) {
|
|
@@ -404,7 +415,7 @@ export function resolveEnvBearerToken() {
|
|
|
404
415
|
if (!token) {
|
|
405
416
|
throw new Error(`ORIZU_TOKEN_FILE (${tokenFile}) is empty.`);
|
|
406
417
|
}
|
|
407
|
-
return token;
|
|
418
|
+
return rememberResolvedBearer(token);
|
|
408
419
|
}
|
|
409
420
|
return null;
|
|
410
421
|
}
|
|
@@ -426,7 +437,7 @@ export function resolveAuthTokenForBaseUrl(baseUrl) {
|
|
|
426
437
|
if (!credentials) {
|
|
427
438
|
throw new Error(`Not logged in for ${baseUrl}. Run \`orizu login --server ${baseUrl}\` (or \`--local\`) first.`);
|
|
428
439
|
}
|
|
429
|
-
return 'accessToken' in credentials ? credentials.accessToken : credentials.apiKey;
|
|
440
|
+
return rememberResolvedBearer('accessToken' in credentials ? credentials.accessToken : credentials.apiKey);
|
|
430
441
|
}
|
|
431
442
|
/**
|
|
432
443
|
* Non-throwing "is any auth available?" check (login --no-prompt-if-logged-in,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-transfer-download.d.ts","sourceRoot":"","sources":["../src/dataset-transfer-download.ts"],"names":[],"mappings":"AAmBA,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyChH"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Effect, Either, Schedule } from 'effect';
|
|
2
|
+
import { writeFileSync } from 'node:fs';
|
|
3
|
+
import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js';
|
|
4
|
+
import { authedFetch } from './http.js';
|
|
5
|
+
class DownloadHttpError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
_tag = 'DownloadHttpError';
|
|
8
|
+
constructor(status, detail) {
|
|
9
|
+
super(`Download failed (HTTP ${status}): ${detail}`);
|
|
10
|
+
this.status = status;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class DownloadTransportError extends Error {
|
|
14
|
+
_tag = 'DownloadTransportError';
|
|
15
|
+
constructor(cause) {
|
|
16
|
+
super('Dataset download transport failed', { cause });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function transferDatasetDownload(datasetId, format, filename) {
|
|
20
|
+
const timeoutMs = datasetTransferTimeoutMs();
|
|
21
|
+
const download = Effect.tryPromise({
|
|
22
|
+
try: async (signal) => {
|
|
23
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/download?format=${encodeURIComponent(format)}`, { signal });
|
|
24
|
+
if (!response.ok)
|
|
25
|
+
throw new DownloadHttpError(response.status, await response.text());
|
|
26
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
27
|
+
},
|
|
28
|
+
catch: cause => {
|
|
29
|
+
if (cause instanceof TypeError)
|
|
30
|
+
return new DownloadTransportError(cause);
|
|
31
|
+
return cause instanceof Error ? cause : new Error('Dataset download request failed', { cause });
|
|
32
|
+
},
|
|
33
|
+
}).pipe(Effect.timeoutFail({
|
|
34
|
+
duration: timeoutMs,
|
|
35
|
+
onTimeout: () => new Error('DATASET_TRANSFER_TIMEOUT: Dataset download exceeded its deadline'),
|
|
36
|
+
}), Effect.retry({
|
|
37
|
+
times: 2,
|
|
38
|
+
schedule: Schedule.spaced(100),
|
|
39
|
+
while: error => error instanceof DownloadTransportError || (error instanceof DownloadHttpError && [429, 502, 503, 504].includes(error.status)),
|
|
40
|
+
}));
|
|
41
|
+
const fileError = (cause) => new Error(`DATASET_DOWNLOAD_FILE: Could not save dataset to ${filename}: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
42
|
+
const write = (bytes) => Effect.try({
|
|
43
|
+
// Wait for the complete response, then retain the CLI's existing in-place
|
|
44
|
+
// write semantics, including inode metadata, hardlinks and symlink targets.
|
|
45
|
+
try: () => writeFileSync(filename, bytes, { mode: 0o600 }),
|
|
46
|
+
catch: fileError,
|
|
47
|
+
});
|
|
48
|
+
const controller = new AbortController();
|
|
49
|
+
const handleInterrupt = () => controller.abort();
|
|
50
|
+
process.once('SIGINT', handleInterrupt);
|
|
51
|
+
process.once('SIGTERM', handleInterrupt);
|
|
52
|
+
try {
|
|
53
|
+
const result = await Effect.runPromise(Effect.either(Effect.flatMap(download, write)), { signal: controller.signal });
|
|
54
|
+
if (Either.isLeft(result))
|
|
55
|
+
throw result.left;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (controller.signal.aborted)
|
|
59
|
+
throw new Error('DATASET_TRANSFER_INTERRUPTED: Dataset download cancelled');
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
process.removeListener('SIGINT', handleInterrupt);
|
|
64
|
+
process.removeListener('SIGTERM', handleInterrupt);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-transfer-policy.d.ts","sourceRoot":"","sources":["../src/dataset-transfer-policy.ts"],"names":[],"mappings":"AAAA,wBAAgB,wBAAwB,IAAI,MAAM,CAOjD"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function datasetTransferTimeoutMs() {
|
|
2
|
+
const configured = process.env.ORIZU_DATASET_TRANSFER_TIMEOUT_MS;
|
|
3
|
+
const timeoutMs = configured === undefined ? 120_000 : Number(configured);
|
|
4
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 300_000) {
|
|
5
|
+
throw new Error('DATASET_TRANSFER_CONFIG: ORIZU_DATASET_TRANSFER_TIMEOUT_MS must be an integer from 1 to 300000');
|
|
6
|
+
}
|
|
7
|
+
return timeoutMs;
|
|
8
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface DatasetUploadResponse {
|
|
2
|
+
dataset: {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
rowCount: number;
|
|
6
|
+
sourceType: string;
|
|
7
|
+
url?: string;
|
|
8
|
+
};
|
|
9
|
+
readmeVersion?: {
|
|
10
|
+
id: string;
|
|
11
|
+
version_num: number;
|
|
12
|
+
created_at: string;
|
|
13
|
+
} | null;
|
|
14
|
+
}
|
|
15
|
+
interface DatasetSnapshot {
|
|
16
|
+
datasetVersion: {
|
|
17
|
+
id: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
type UploadRows = Array<Record<string, unknown>>;
|
|
21
|
+
type UploadSource = {
|
|
22
|
+
kind: 'jsonl';
|
|
23
|
+
file: string;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'rows';
|
|
26
|
+
rows: UploadRows;
|
|
27
|
+
sourceType: 'csv' | 'json' | 'jsonl';
|
|
28
|
+
};
|
|
29
|
+
export interface DatasetUploadOptions {
|
|
30
|
+
project: string;
|
|
31
|
+
name: string;
|
|
32
|
+
source: UploadSource;
|
|
33
|
+
readmeMarkdown: string | null;
|
|
34
|
+
json: boolean;
|
|
35
|
+
onChunk: (index: number, rowCount: number) => void;
|
|
36
|
+
}
|
|
37
|
+
export declare function transferDatasetUpload(options: DatasetUploadOptions): Promise<{
|
|
38
|
+
data: DatasetUploadResponse;
|
|
39
|
+
version: DatasetSnapshot;
|
|
40
|
+
}>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=dataset-transfer-upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-transfer-upload.d.ts","sourceRoot":"","sources":["../src/dataset-transfer-upload.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACzF,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC/E;AAED,UAAU,eAAe;IAAG,cAAc,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE;AAE5D,KAAK,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAChD,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,CAAA;AAE9H,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,YAAY,CAAA;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CACnD;AAoHD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,CAAC,CAEvI"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Context, Effect, Either, Layer } from 'effect';
|
|
2
|
+
import { ensureDatasetUploadSnapshot } from './dataset-upload-snapshot.js';
|
|
3
|
+
import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js';
|
|
4
|
+
import { extractErrorMessage } from './error-response.js';
|
|
5
|
+
import { authedFetch } from './http.js';
|
|
6
|
+
import { parseJsonResponse, sanitizeTerminalText } from './json-response.js';
|
|
7
|
+
import { streamJsonlRowChunks } from './jsonl-stream.js';
|
|
8
|
+
class DatasetWriteFailure extends Error {
|
|
9
|
+
outcome;
|
|
10
|
+
_tag = 'DatasetWriteFailure';
|
|
11
|
+
constructor(message, outcome, options) {
|
|
12
|
+
super(message, options);
|
|
13
|
+
this.outcome = outcome;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class DatasetInputFailure extends Error {
|
|
17
|
+
_tag = 'DatasetInputFailure';
|
|
18
|
+
constructor(cause) { super(cause instanceof Error ? cause.message : String(cause), { cause }); }
|
|
19
|
+
}
|
|
20
|
+
const DatasetUploadApi = Context.GenericTag('orizu/DatasetUploadApi');
|
|
21
|
+
// Only the existing authentication client's rejected-token refresh may replay a
|
|
22
|
+
// request. No transient/transport retries belong around these non-idempotent writes.
|
|
23
|
+
function post(path, body, context, prefix, timeoutMs) {
|
|
24
|
+
return Effect.suspend(() => {
|
|
25
|
+
let requestStarted = false;
|
|
26
|
+
return Effect.tryPromise({
|
|
27
|
+
try: async (signal) => {
|
|
28
|
+
const response = await authedFetch(path, {
|
|
29
|
+
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify(body), signal,
|
|
31
|
+
}, () => { requestStarted = true; });
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new DatasetWriteFailure(`${prefix}: ${await extractErrorMessage(response)}`, response.status >= 500 ? 'unknown' : 'rejected');
|
|
34
|
+
}
|
|
35
|
+
return await parseJsonResponse(response, context);
|
|
36
|
+
},
|
|
37
|
+
catch: cause => cause instanceof DatasetWriteFailure ? cause : new DatasetWriteFailure(cause instanceof Error ? cause.message : String(cause), requestStarted ? 'unknown' : 'rejected', { cause }),
|
|
38
|
+
}).pipe(Effect.timeoutFail({
|
|
39
|
+
duration: timeoutMs,
|
|
40
|
+
onTimeout: () => new DatasetWriteFailure(`DATASET_TRANSFER_TIMEOUT: ${context} exceeded its deadline`, requestStarted ? 'unknown' : 'rejected'),
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function liveUploadApi(options) {
|
|
45
|
+
const timeoutMs = datasetTransferTimeoutMs();
|
|
46
|
+
return Layer.succeed(DatasetUploadApi, {
|
|
47
|
+
create: (rows, sourceType) => post('/api/cli/datasets/upload', {
|
|
48
|
+
projectSlug: options.project, name: options.name, rows, sourceType,
|
|
49
|
+
...(options.readmeMarkdown !== null ? { readmeMarkdown: options.readmeMarkdown } : {}),
|
|
50
|
+
}, 'Dataset upload', 'Upload failed', timeoutMs),
|
|
51
|
+
append: (id, rows) => post(`/api/cli/datasets/${encodeURIComponent(id)}/rows`, { rows }, 'Dataset append', 'Append failed', timeoutMs),
|
|
52
|
+
snapshot: id => post(`/api/cli/datasets/${encodeURIComponent(id)}/versions`, {}, 'Dataset version create', 'Failed to create dataset version', timeoutMs),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async function expectedPromise(program) {
|
|
56
|
+
const result = await Effect.runPromise(Effect.either(program));
|
|
57
|
+
if (Either.isLeft(result))
|
|
58
|
+
throw result.left;
|
|
59
|
+
return result.right;
|
|
60
|
+
}
|
|
61
|
+
function uploadProgram(options) {
|
|
62
|
+
return Effect.gen(function* () {
|
|
63
|
+
const api = yield* DatasetUploadApi;
|
|
64
|
+
const chunks = yield* Effect.acquireRelease(Effect.sync(() => options.source.kind === 'jsonl'
|
|
65
|
+
? streamJsonlRowChunks(options.source.file)
|
|
66
|
+
: (async function* () { if (options.source.kind === 'rows')
|
|
67
|
+
yield options.source.rows; })()), iterator => Effect.promise(async () => { await iterator.return(undefined); }));
|
|
68
|
+
let data;
|
|
69
|
+
let confirmedRows = 0;
|
|
70
|
+
let chunkIndex = 0;
|
|
71
|
+
while (true) {
|
|
72
|
+
const next = yield* Effect.tryPromise({ try: () => chunks.next(), catch: cause => new DatasetInputFailure(cause) }).pipe(Effect.mapError(error => new Error(data
|
|
73
|
+
? `Upload stopped while reading the next JSONL chunk: ${error.message}\nDataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) was created and ${confirmedRows} rows were uploaded. Fix the file, remove the first ${confirmedRows} rows, and run orizu datasets append --dataset ${data.dataset.id} --file <remaining-file>.`
|
|
74
|
+
: error.message)));
|
|
75
|
+
if (next.done)
|
|
76
|
+
break;
|
|
77
|
+
chunkIndex++;
|
|
78
|
+
if (options.source.kind === 'jsonl')
|
|
79
|
+
yield* Effect.sync(() => options.onChunk(chunkIndex, next.value.length));
|
|
80
|
+
const write = data
|
|
81
|
+
? api.append(data.dataset.id, next.value).pipe(Effect.map(result => ({
|
|
82
|
+
data: { ...data, dataset: { ...data.dataset, rowCount: result.dataset.rowCount } },
|
|
83
|
+
added: result.appendedCount,
|
|
84
|
+
})))
|
|
85
|
+
: api.create(next.value, options.source.kind === 'jsonl' ? 'jsonl' : options.source.sourceType).pipe(Effect.map(result => ({ data: result, added: result.dataset.rowCount })));
|
|
86
|
+
const result = yield* write.pipe(Effect.mapError(error => {
|
|
87
|
+
if (error.outcome === 'unknown') {
|
|
88
|
+
return new Error(`DATASET_UPLOAD_UNCERTAIN: Chunk ${chunkIndex} may have committed, but its outcome could not be confirmed: ${error.message}\n` +
|
|
89
|
+
(data ? `Dataset ${sanitizeTerminalText(data.dataset.id)} has ${confirmedRows} confirmed uploaded rows. ` : 'Dataset creation could not be confirmed. ') +
|
|
90
|
+
'Inspect the dataset and reconcile row IDs before retrying; do not replay this chunk blindly.');
|
|
91
|
+
}
|
|
92
|
+
return new Error(data
|
|
93
|
+
? `Chunk ${chunkIndex} failed: ${error.message}\nDataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) was created and ${confirmedRows} rows were uploaded. To retry, remove the first ${confirmedRows} rows from your file and run orizu datasets append --dataset ${data.dataset.id} --file <remaining-file>.`
|
|
94
|
+
: error.message);
|
|
95
|
+
}));
|
|
96
|
+
data = result.data;
|
|
97
|
+
confirmedRows += result.added;
|
|
98
|
+
}
|
|
99
|
+
if (!data)
|
|
100
|
+
return yield* Effect.fail(new Error('Dataset file contains no rows'));
|
|
101
|
+
const version = yield* Effect.tryPromise({
|
|
102
|
+
try: () => ensureDatasetUploadSnapshot(data.dataset, options.json, id => expectedPromise(api.snapshot(id))),
|
|
103
|
+
catch: cause => cause instanceof Error ? cause : new Error(String(cause)),
|
|
104
|
+
});
|
|
105
|
+
return { data, version };
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export function transferDatasetUpload(options) {
|
|
109
|
+
return expectedPromise(uploadProgram(options).pipe(Effect.scoped, Effect.provide(liveUploadApi(options))));
|
|
110
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface FeedbackCliIo {
|
|
2
|
+
json: boolean;
|
|
3
|
+
print: (message: string) => void;
|
|
4
|
+
printErr: (message: string) => void;
|
|
5
|
+
fetcher?: (path: string, init?: RequestInit) => Promise<Response>;
|
|
6
|
+
configDir?: string;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
homeDir?: string;
|
|
10
|
+
hasAuth?: (baseUrl: string) => boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveFeedbackTimeoutMs(baseUrl: string): number;
|
|
13
|
+
export declare function feedbackCommand(args: string[], io: FeedbackCliIo): Promise<number>;
|
|
14
|
+
//# sourceMappingURL=feedback-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback-cli.d.ts","sourceRoot":"","sources":["../src/feedback-cli.ts"],"names":[],"mappings":"AAyDA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAA;CACvC;AAwaD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWhE;AAiCD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAmOxF"}
|