orizu 0.5.17 → 0.5.19
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 +15 -0
- package/dist/archive-artifacts-cli.js +1 -3
- package/dist/artifact-pull.js +1 -3
- package/dist/diff-comments-cli.js +120 -32
- package/dist/error-response.js +17 -17
- package/dist/github-setup.js +3 -11
- package/dist/help.js +22 -2
- package/dist/index.js +43 -107
- package/dist/json-response.js +21 -0
- package/dist/markdown-report-input.js +67 -0
- package/dist/optimization-export-cli.js +30 -0
- package/dist/optimizations-list-cli.js +1 -3
- package/dist/prompt-draft-push.js +7 -5
- package/dist/prompt-length-wire.js +30 -0
- package/dist/prompt-report-cli.js +86 -0
- package/dist/prompt-summary-table.js +37 -0
- package/dist/scorer-draft-push.js +1 -22
- package/dist/scores-accept-cli.js +1 -3
- package/dist/zip-draft-push.js +1 -24
- package/package.json +1 -1
- package/src/archive-artifacts-cli.ts +1 -7
- package/src/artifact-pull.ts +1 -4
- package/src/diff-comments-cli.ts +177 -37
- package/src/error-response.ts +23 -21
- package/src/github-setup.ts +3 -12
- package/src/help.ts +22 -2
- package/src/index.ts +54 -132
- package/src/json-response.ts +37 -0
- package/src/markdown-report-input.ts +80 -0
- package/src/optimization-export-cli.ts +72 -0
- package/src/optimizations-list-cli.ts +1 -4
- package/src/prompt-draft-push.ts +8 -6
- package/src/prompt-length-wire.ts +65 -0
- package/src/prompt-report-cli.ts +135 -0
- package/src/prompt-summary-table.ts +57 -0
- package/src/scorer-draft-push.ts +1 -6
- package/src/scores-accept-cli.ts +1 -4
- package/src/zip-draft-push.ts +1 -6
- package/vendor/skills/orizu-cli/SKILL.md +1 -0
- package/vendor/skills/orizu-cli/references/cli-reference.md +15 -1
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +19 -0
package/README.md
CHANGED
|
@@ -173,6 +173,14 @@ orizu logout
|
|
|
173
173
|
<td>Archive or restore a prompt</td>
|
|
174
174
|
<td><code>orizu prompts archive <promptIdOrName> --project ops-eval/support-qa</code><br><code>orizu prompts restore <promptIdOrName> --project ops-eval/support-qa</code></td>
|
|
175
175
|
</tr>
|
|
176
|
+
<tr>
|
|
177
|
+
<td>Push a prompt with a report</td>
|
|
178
|
+
<td><code>orizu prompts push ./prompts/support --project ops-eval/support-qa --report-file ./prompt-report.md</code></td>
|
|
179
|
+
</tr>
|
|
180
|
+
<tr>
|
|
181
|
+
<td>Upload a report for an existing prompt version</td>
|
|
182
|
+
<td><code>orizu prompts report set --prompt <promptIdOrName> --project ops-eval/support-qa --version 3 --report-file ./prompt-report.md</code><br><code>prompts report upload</code> is an exact alias; select exactly one of <code>--version</code> or <code>--version-id</code>.</td>
|
|
183
|
+
</tr>
|
|
176
184
|
<tr>
|
|
177
185
|
<td colspan="2"><strong>Report comments</strong></td>
|
|
178
186
|
</tr>
|
|
@@ -184,6 +192,13 @@ orizu logout
|
|
|
184
192
|
<td>Add or update comments</td>
|
|
185
193
|
<td><code>orizu comments add --run <runId> --body @comment.md --anchor "Summary" --lines 4:6</code><br><code>orizu comments reply <commentId> --body "Fixed"</code><br><code>orizu comments resolve <commentId></code></td>
|
|
186
194
|
</tr>
|
|
195
|
+
<tr>
|
|
196
|
+
<td colspan="2"><strong>Diff comments</strong></td>
|
|
197
|
+
</tr>
|
|
198
|
+
<tr>
|
|
199
|
+
<td>Export diff comments</td>
|
|
200
|
+
<td><code>orizu comments diff --run <optimizationRunId> [--from <candidateId> --to <candidateId>] [--detail hunk|diff|full] [--json]</code><br><code>orizu comments diff --prompt <promptId> [--from <version> --to <version>] [--detail hunk|diff|full] [--json]</code></td>
|
|
201
|
+
</tr>
|
|
187
202
|
<tr>
|
|
188
203
|
<td colspan="2"><strong>Datasets</strong></td>
|
|
189
204
|
</tr>
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { authedFetch } from './http.js';
|
|
9
9
|
import { extractErrorMessage } from './error-response.js';
|
|
10
|
+
import { sanitizeTerminalText } from './json-response.js';
|
|
10
11
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
11
12
|
const ARTIFACT_TYPES = {
|
|
12
13
|
app: 'app',
|
|
@@ -31,9 +32,6 @@ function argValue(args, flag) {
|
|
|
31
32
|
}
|
|
32
33
|
return args[index + 1];
|
|
33
34
|
}
|
|
34
|
-
function sanitizeTerminalText(value) {
|
|
35
|
-
return String(value).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '');
|
|
36
|
-
}
|
|
37
35
|
function columnWidths(headers, rows) {
|
|
38
36
|
return headers.map((header, index) => Math.max(header.length, ...rows.map(row => row[index]?.length || 0)));
|
|
39
37
|
}
|
package/dist/artifact-pull.js
CHANGED
|
@@ -16,11 +16,9 @@ import { mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from 'fs';
|
|
|
16
16
|
import { tmpdir } from 'os';
|
|
17
17
|
import { dirname, join } from 'path';
|
|
18
18
|
import { authedFetch } from './http.js';
|
|
19
|
+
import { sanitizeTerminalText } from './json-response.js';
|
|
19
20
|
// Shared with the runners exec download path in index.ts.
|
|
20
21
|
export const ARTIFACT_MAX_BYTES = 25 * 1024 * 1024;
|
|
21
|
-
function sanitizeTerminalText(value) {
|
|
22
|
-
return String(value).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '');
|
|
23
|
-
}
|
|
24
22
|
function expandHomePath(path) {
|
|
25
23
|
if (path.startsWith('~/')) {
|
|
26
24
|
const home = process.env.HOME || '';
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { extractErrorMessage, isJsonErrorResponsePayload, } from './error-response.js';
|
|
2
|
+
import { sanitizeHumanInlineText } from './json-response.js';
|
|
3
|
+
import { formatFiniteLengthCount, } from './prompt-length-wire.js';
|
|
1
4
|
const DETAIL_LEVELS = ['hunk', 'diff', 'full'];
|
|
5
|
+
const CONTEXT_UNAVAILABLE_WITHOUT_REASON = ' Context unavailable (no reason supplied)';
|
|
2
6
|
function isCandidatePair(pair) {
|
|
3
7
|
return 'candidateId' in pair.from;
|
|
4
8
|
}
|
|
@@ -8,11 +12,91 @@ function usage() {
|
|
|
8
12
|
function sanitizeHumanText(ctx, value) {
|
|
9
13
|
return ctx.sanitizeTerminalText(value).replaceAll('\r', '');
|
|
10
14
|
}
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
// Quote untrusted multiline blocks so none of their rows can impersonate a
|
|
16
|
+
// genuine hunk row, whose marker begins immediately after the four-space indent.
|
|
17
|
+
function printHumanTextBlock(ctx, value) {
|
|
18
|
+
for (const line of sanitizeHumanText(ctx, value).split('\n')) {
|
|
19
|
+
ctx.printLine(` │ ${line}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function hasExplicitlyEmptyJsonError(response, rawBody) {
|
|
23
|
+
try {
|
|
24
|
+
const payload = JSON.parse(rawBody);
|
|
25
|
+
return isJsonErrorResponsePayload(response, payload) && payload.error.length === 0;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function extractDiffCommentsErrorMessage(response) {
|
|
32
|
+
const statusFallback = `HTTP ${response.status}`;
|
|
33
|
+
try {
|
|
34
|
+
const rawBody = await response.text();
|
|
35
|
+
if (hasExplicitlyEmptyJsonError(response, rawBody)) {
|
|
36
|
+
return statusFallback;
|
|
37
|
+
}
|
|
38
|
+
const copiedResponse = new Response(rawBody, { headers: response.headers });
|
|
39
|
+
const extractedMessage = await extractErrorMessage(copiedResponse);
|
|
40
|
+
return extractedMessage.trim() ? extractedMessage : statusFallback;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return statusFallback;
|
|
44
|
+
}
|
|
13
45
|
}
|
|
14
46
|
function humanLineNumber(ctx, line) {
|
|
15
|
-
return line === null
|
|
47
|
+
return line === null
|
|
48
|
+
? '-'
|
|
49
|
+
: sanitizeHumanInlineText(ctx.sanitizeTerminalText, String(line + 1));
|
|
50
|
+
}
|
|
51
|
+
function formatStats(stats) {
|
|
52
|
+
const tokens = formatFiniteLengthCount(stats.tokens);
|
|
53
|
+
return `${tokens === null ? '—' : `~${tokens}`} tokens · ` +
|
|
54
|
+
`${formatFiniteLengthCount(stats.lines) ?? '—'} lines · ` +
|
|
55
|
+
`${formatFiniteLengthCount(stats.chars) ?? '—'} chars · ` +
|
|
56
|
+
`${formatFiniteLengthCount(stats.words) ?? '—'} words`;
|
|
57
|
+
}
|
|
58
|
+
function formatDeltaValue(value, metric, signed = false) {
|
|
59
|
+
const formatted = formatFiniteLengthCount(value, signed);
|
|
60
|
+
if (formatted === null)
|
|
61
|
+
return '—';
|
|
62
|
+
return metric === 'tokens' ? `~${formatted}` : formatted;
|
|
63
|
+
}
|
|
64
|
+
function printLengthMeasurements(ctx, pair) {
|
|
65
|
+
function unavailableSuffix(reason) {
|
|
66
|
+
if (typeof reason !== 'string' || reason.length === 0)
|
|
67
|
+
return '';
|
|
68
|
+
return ` (${sanitizeHumanInlineText(ctx.sanitizeTerminalText, reason)})`;
|
|
69
|
+
}
|
|
70
|
+
if (!pair.lengthStats && !pair.lengthDelta) {
|
|
71
|
+
ctx.printLine(` Length: unavailable${unavailableSuffix(pair.lengthDeltaUnavailableReason ?? pair.degraded?.reason)}`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (pair.lengthStats) {
|
|
75
|
+
ctx.printLine(pair.lengthStats.from
|
|
76
|
+
? ` From: ${formatStats(pair.lengthStats.from)}`
|
|
77
|
+
: ` From: unavailable${unavailableSuffix(pair.lengthStats.fromUnavailableReason)}`);
|
|
78
|
+
ctx.printLine(pair.lengthStats.to
|
|
79
|
+
? ` To: ${formatStats(pair.lengthStats.to)}`
|
|
80
|
+
: ` To: unavailable${unavailableSuffix(pair.lengthStats.toUnavailableReason)}`);
|
|
81
|
+
}
|
|
82
|
+
if (!pair.lengthDelta) {
|
|
83
|
+
ctx.printLine(` Length delta: unavailable${unavailableSuffix(pair.lengthDeltaUnavailableReason ?? pair.degraded?.reason)}`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (pair.lengthDelta.splitUnavailableReason) {
|
|
87
|
+
ctx.printLine(` Split unavailable: ${sanitizeHumanInlineText(ctx.sanitizeTerminalText, pair.lengthDelta.splitUnavailableReason)}`);
|
|
88
|
+
}
|
|
89
|
+
for (const [metric, label] of [
|
|
90
|
+
['tokens', 'Tokens'],
|
|
91
|
+
['lines', 'Lines'],
|
|
92
|
+
['chars', 'Chars'],
|
|
93
|
+
['words', 'Words'],
|
|
94
|
+
]) {
|
|
95
|
+
const delta = pair.lengthDelta[metric];
|
|
96
|
+
ctx.printLine(` ${label}: removed ${formatDeltaValue(delta.removed, metric)} · ` +
|
|
97
|
+
`added ${formatDeltaValue(delta.added, metric)} · ` +
|
|
98
|
+
`net ${formatDeltaValue(delta.net, metric, true)}`);
|
|
99
|
+
}
|
|
16
100
|
}
|
|
17
101
|
function buildParams(ctx) {
|
|
18
102
|
const runId = ctx.getArg('--run');
|
|
@@ -29,9 +113,15 @@ function buildParams(ctx) {
|
|
|
29
113
|
]) {
|
|
30
114
|
ctx.rejectDashPrefixedOptionValue(name, value);
|
|
31
115
|
}
|
|
116
|
+
if (runId === '' || promptId === '') {
|
|
117
|
+
throw new Error(usage());
|
|
118
|
+
}
|
|
32
119
|
if (Number(Boolean(runId)) + Number(Boolean(promptId)) !== 1) {
|
|
33
120
|
throw new Error(usage());
|
|
34
121
|
}
|
|
122
|
+
if (from === '' || to === '') {
|
|
123
|
+
throw new Error('--from and --to must not be empty');
|
|
124
|
+
}
|
|
35
125
|
if (Boolean(from) !== Boolean(to)) {
|
|
36
126
|
throw new Error('--from and --to must be provided together');
|
|
37
127
|
}
|
|
@@ -65,15 +155,15 @@ function printDiffComments(ctx, payload) {
|
|
|
65
155
|
const to = hasCandidateIds ? pair.to.candidateId : String(pair.to.version);
|
|
66
156
|
ctx.printLine('');
|
|
67
157
|
ctx.printLine(`${hasCandidateIds ? 'Candidates' : 'Versions'} ` +
|
|
68
|
-
`${sanitizeHumanInlineText(ctx, from)} →
|
|
158
|
+
`${sanitizeHumanInlineText(ctx.sanitizeTerminalText, from)} → ` +
|
|
159
|
+
`${sanitizeHumanInlineText(ctx.sanitizeTerminalText, to)}`);
|
|
160
|
+
printLengthMeasurements(ctx, pair);
|
|
69
161
|
if (pair.degraded) {
|
|
70
|
-
ctx.printLine(` Context unavailable: ${
|
|
162
|
+
ctx.printLine(` Context unavailable: ${sanitizeHumanInlineText(ctx.sanitizeTerminalText, pair.degraded.reason)}`);
|
|
71
163
|
}
|
|
72
164
|
if (pair.diff !== null) {
|
|
73
165
|
ctx.printLine(' Diff:');
|
|
74
|
-
|
|
75
|
-
ctx.printLine(` ${line}`);
|
|
76
|
-
}
|
|
166
|
+
printHumanTextBlock(ctx, pair.diff);
|
|
77
167
|
}
|
|
78
168
|
if (pair.bodies !== null) {
|
|
79
169
|
for (const [label, body] of [
|
|
@@ -81,24 +171,31 @@ function printDiffComments(ctx, payload) {
|
|
|
81
171
|
['To body', pair.bodies.to],
|
|
82
172
|
]) {
|
|
83
173
|
ctx.printLine(` ${label}:`);
|
|
84
|
-
|
|
85
|
-
ctx.printLine(` ${line}`);
|
|
86
|
-
}
|
|
174
|
+
printHumanTextBlock(ctx, body);
|
|
87
175
|
}
|
|
88
176
|
}
|
|
89
177
|
for (const comment of pair.comments) {
|
|
90
178
|
const authorName = comment.author?.person?.name || comment.author?.name || 'Unknown';
|
|
91
|
-
ctx.printLine(` ${sanitizeHumanInlineText(ctx, comment.id)} · ` +
|
|
92
|
-
`${sanitizeHumanInlineText(ctx, authorName)} · ` +
|
|
93
|
-
`${sanitizeHumanInlineText(ctx, comment.createdAt)} · ` +
|
|
94
|
-
`${sanitizeHumanInlineText(ctx, comment.anchor.side)} line ` +
|
|
95
|
-
`${sanitizeHumanInlineText(ctx, String(comment.anchor.line + 1))}`);
|
|
96
|
-
|
|
97
|
-
|
|
179
|
+
ctx.printLine(` ${sanitizeHumanInlineText(ctx.sanitizeTerminalText, comment.id)} · ` +
|
|
180
|
+
`${sanitizeHumanInlineText(ctx.sanitizeTerminalText, authorName)} · ` +
|
|
181
|
+
`${sanitizeHumanInlineText(ctx.sanitizeTerminalText, comment.createdAt)} · ` +
|
|
182
|
+
`${sanitizeHumanInlineText(ctx.sanitizeTerminalText, comment.anchor.side)} line ` +
|
|
183
|
+
`${sanitizeHumanInlineText(ctx.sanitizeTerminalText, String(comment.anchor.line + 1))}`);
|
|
184
|
+
printHumanTextBlock(ctx, comment.body);
|
|
185
|
+
if (comment.contextUnavailableReason) {
|
|
186
|
+
ctx.printLine(` Context unavailable: ${sanitizeHumanInlineText(ctx.sanitizeTerminalText, comment.contextUnavailableReason)}`);
|
|
187
|
+
if (comment.context) {
|
|
188
|
+
ctx.printLine(` Anchored line: ${sanitizeHumanInlineText(ctx.sanitizeTerminalText, comment.context.lineText)}`);
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
98
191
|
}
|
|
99
192
|
if (!comment.context) {
|
|
100
|
-
ctx.printLine(
|
|
101
|
-
|
|
193
|
+
ctx.printLine(CONTEXT_UNAVAILABLE_WITHOUT_REASON);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (!comment.context.hunk) {
|
|
197
|
+
ctx.printLine(CONTEXT_UNAVAILABLE_WITHOUT_REASON);
|
|
198
|
+
ctx.printLine(` Anchored line: ${sanitizeHumanInlineText(ctx.sanitizeTerminalText, comment.context.lineText)}`);
|
|
102
199
|
continue;
|
|
103
200
|
}
|
|
104
201
|
for (const line of comment.context.hunk.lines) {
|
|
@@ -109,7 +206,7 @@ function printDiffComments(ctx, payload) {
|
|
|
109
206
|
const marker = isAnchored ? '>' : ' ';
|
|
110
207
|
ctx.printLine(` ${marker} old:${humanLineNumber(ctx, line.oldLine)} ` +
|
|
111
208
|
`new:${humanLineNumber(ctx, line.newLine)} | ` +
|
|
112
|
-
`${prefix}${
|
|
209
|
+
`${prefix}${sanitizeHumanInlineText(ctx.sanitizeTerminalText, line.text)}`);
|
|
113
210
|
}
|
|
114
211
|
}
|
|
115
212
|
}
|
|
@@ -118,17 +215,8 @@ export async function diffCommentsCommand(ctx) {
|
|
|
118
215
|
const params = buildParams(ctx);
|
|
119
216
|
const response = await ctx.authedFetch(`/api/cli/diff-comments?${params.toString()}`);
|
|
120
217
|
if (!response.ok) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const payload = await response.clone().json();
|
|
124
|
-
if (typeof payload.error === 'string' && payload.error.trim()) {
|
|
125
|
-
message = sanitizeHumanInlineText(ctx, payload.error);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
catch {
|
|
129
|
-
// Keep the status-only error for non-JSON responses.
|
|
130
|
-
}
|
|
131
|
-
throw new Error(`Failed to fetch diff comments: ${message}`);
|
|
218
|
+
const message = await extractDiffCommentsErrorMessage(response);
|
|
219
|
+
throw new Error(`Failed to fetch diff comments: ${sanitizeHumanInlineText(ctx.sanitizeTerminalText, message)}`);
|
|
132
220
|
}
|
|
133
221
|
const payload = await ctx.parseJsonResponse(response, 'Diff comments');
|
|
134
222
|
if (ctx.hasJsonFlag()) {
|
package/dist/error-response.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
+
import { sanitizeTerminalText } from './json-response.js';
|
|
1
2
|
const ERROR_BODY_PREVIEW_LIMIT = 180;
|
|
3
|
+
export function isJsonErrorResponsePayload(response, payload) {
|
|
4
|
+
const contentType = response.headers.get('content-type') || '';
|
|
5
|
+
return (contentType.includes('application/json') &&
|
|
6
|
+
payload !== null &&
|
|
7
|
+
typeof payload === 'object' &&
|
|
8
|
+
!Array.isArray(payload) &&
|
|
9
|
+
typeof payload.error === 'string');
|
|
10
|
+
}
|
|
2
11
|
function boundedTerminalPreview(value) {
|
|
3
|
-
return
|
|
4
|
-
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '')
|
|
5
|
-
.slice(0, ERROR_BODY_PREVIEW_LIMIT);
|
|
12
|
+
return sanitizeTerminalText(value).slice(0, ERROR_BODY_PREVIEW_LIMIT);
|
|
6
13
|
}
|
|
7
14
|
export async function extractErrorMessage(response) {
|
|
8
|
-
const contentType = response.headers.get('content-type') || '';
|
|
9
15
|
const rawBody = await response.text();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
typeof payload === 'object' &&
|
|
15
|
-
!Array.isArray(payload) &&
|
|
16
|
-
typeof payload.error === 'string' &&
|
|
17
|
-
payload.error.length > 0) {
|
|
18
|
-
return boundedTerminalPreview(payload.error);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// Fall through to raw body
|
|
16
|
+
try {
|
|
17
|
+
const payload = JSON.parse(rawBody);
|
|
18
|
+
if (isJsonErrorResponsePayload(response, payload) && payload.error.length > 0) {
|
|
19
|
+
return boundedTerminalPreview(payload.error);
|
|
23
20
|
}
|
|
24
21
|
}
|
|
22
|
+
catch {
|
|
23
|
+
// Fall through to raw body
|
|
24
|
+
}
|
|
25
25
|
return boundedTerminalPreview(rawBody);
|
|
26
26
|
}
|
package/dist/github-setup.js
CHANGED
|
@@ -17,16 +17,8 @@ import { spawnSync } from 'child_process';
|
|
|
17
17
|
import { existsSync, mkdirSync } from 'fs';
|
|
18
18
|
import { join } from 'path';
|
|
19
19
|
import { authedFetch } from './http.js';
|
|
20
|
+
import { sanitizeTerminalText } from './json-response.js';
|
|
20
21
|
import { assertWorkspaceDirUsable, workspaceExists } from './workspace.js';
|
|
21
|
-
/**
|
|
22
|
-
* Strip terminal control characters from server-supplied strings before they
|
|
23
|
-
* are printed (mirrors index.ts `sanitizeTerminalText`), so a hostile or
|
|
24
|
-
* mangled provisioning `warnings[]`/`defaultBranch` value can't inject escape
|
|
25
|
-
* sequences into the user's terminal.
|
|
26
|
-
*/
|
|
27
|
-
function sanitizeServerText(value) {
|
|
28
|
-
return String(value).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '');
|
|
29
|
-
}
|
|
30
22
|
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
31
23
|
// ALI-1141: long enough for a slow org-picker + repo-selection walk, but the
|
|
32
24
|
// wait MUST be finite — when the App is already installed in the target org,
|
|
@@ -231,10 +223,10 @@ export async function runHostedAttach(options, io) {
|
|
|
231
223
|
repoFullName = data.repo;
|
|
232
224
|
provisioned = true;
|
|
233
225
|
if (data.rulesetApplied === true) {
|
|
234
|
-
io.print(` Default-branch protection applied (ruleset "orizu-default-branch-protection"${data.defaultBranch ? ` on ${
|
|
226
|
+
io.print(` Default-branch protection applied (ruleset "orizu-default-branch-protection"${data.defaultBranch ? ` on ${sanitizeTerminalText(data.defaultBranch)}` : ''}).`);
|
|
235
227
|
}
|
|
236
228
|
for (const warning of data.warnings ?? []) {
|
|
237
|
-
io.print(` ⚠ ${
|
|
229
|
+
io.print(` ⚠ ${sanitizeTerminalText(warning)}`);
|
|
238
230
|
}
|
|
239
231
|
}
|
|
240
232
|
const cleanUrl = `https://github.com/${repoFullName}.git`;
|
package/dist/help.js
CHANGED
|
@@ -337,10 +337,30 @@ export const COMMAND_DOCS = [
|
|
|
337
337
|
},
|
|
338
338
|
{
|
|
339
339
|
path: ['prompts', 'push'],
|
|
340
|
-
usage: 'orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--session <session-id>] [--json]',
|
|
340
|
+
usage: 'orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--session <session-id>] [--report-file <path>] [--json]',
|
|
341
341
|
summary: 'Push a local prompt artifact directory as a new version (with --session: a commit-first git draft).',
|
|
342
342
|
group: 'Prompts and judges',
|
|
343
343
|
},
|
|
344
|
+
{
|
|
345
|
+
path: ['prompts', 'report', 'set'],
|
|
346
|
+
usage: 'orizu prompts report set --prompt <id-or-name> --project <team/project> (--version <n> | --version-id <id>) (--report <markdown|@file> | --report-file <path>) [--json]',
|
|
347
|
+
summary: 'Upload or replace the Markdown report for one explicit prompt version.',
|
|
348
|
+
group: 'Prompts and judges',
|
|
349
|
+
aliases: [['prompts', 'report', 'upload']],
|
|
350
|
+
options: [
|
|
351
|
+
{ name: '--prompt <id-or-name>', help: 'Prompt ID or name.', required: true },
|
|
352
|
+
{ name: '--project <team/project>', help: 'Project slug containing the prompt.', required: true },
|
|
353
|
+
{ name: '--version <n>', help: 'Positive prompt version number.' },
|
|
354
|
+
{ name: '--version-id <id>', help: 'Explicit prompt version ID.' },
|
|
355
|
+
{ name: '--report <markdown|@file>', help: 'Inline Markdown or an @file reference.' },
|
|
356
|
+
{ name: '--report-file <path>', help: 'Markdown file to upload.' },
|
|
357
|
+
{ name: '--json', help: 'Emit the updated prompt report payload.' },
|
|
358
|
+
],
|
|
359
|
+
examples: [
|
|
360
|
+
'orizu prompts report set --prompt Generator --project core/evals --version 3 --report-file ./report.md',
|
|
361
|
+
'orizu prompts report upload --prompt Generator --project core/evals --version-id <id> --report @./report.md',
|
|
362
|
+
],
|
|
363
|
+
},
|
|
344
364
|
{
|
|
345
365
|
path: ['prompts', 'labels', 'set'],
|
|
346
366
|
usage: 'orizu prompts labels set <prompt-name> <label> --version <version-id> [--project <team/project>] [--json]',
|
|
@@ -373,7 +393,7 @@ export const COMMAND_DOCS = [
|
|
|
373
393
|
},
|
|
374
394
|
{
|
|
375
395
|
path: ['judges', 'push'],
|
|
376
|
-
usage: 'orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--session <session-id>] [--json]',
|
|
396
|
+
usage: 'orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--session <session-id>] [--report-file <path>] [--json]',
|
|
377
397
|
summary: 'Push a local judge artifact directory as a new version (with --session: a commit-first git draft).',
|
|
378
398
|
group: 'Prompts and judges',
|
|
379
399
|
},
|
package/dist/index.js
CHANGED
|
@@ -41,12 +41,18 @@ import { hostedCommand } from './hosted-session-cli.js';
|
|
|
41
41
|
import { workspaceSyncCommand } from './workspace-sync.js';
|
|
42
42
|
import { runGitCredentialInvocation } from './git-credential.js';
|
|
43
43
|
import { pushPromptDraft } from './prompt-draft-push.js';
|
|
44
|
+
import { readMarkdownReportInput } from './markdown-report-input.js';
|
|
45
|
+
import { promptPushErrorMessage, promptReportCommand } from './prompt-report-cli.js';
|
|
46
|
+
import { printPromptSummaryTable } from './prompt-summary-table.js';
|
|
44
47
|
import { assertSnapshotManifestConfined, verifyGepaRunnerDirsFromArgs, verifyRunnerDirRegistered } from './runner-dir-verify.js';
|
|
45
48
|
import { runScorersRegister } from './scorer-draft-push.js';
|
|
46
49
|
import { runZipArtifactPush } from './zip-draft-push.js';
|
|
47
50
|
import { runGithubLink, runInteractiveHostedSetup } from './github-setup.js';
|
|
51
|
+
import { parseJsonResponse, sanitizeTerminalText } from './json-response.js';
|
|
48
52
|
import { reportCommentsCommand, throwDeprecatedPromptCommentsCommand, } from './report-comments-cli.js';
|
|
49
53
|
import { diffCommentsCommand } from './diff-comments-cli.js';
|
|
54
|
+
import { exportOptimizationRunCommand } from './optimization-export-cli.js';
|
|
55
|
+
export { parseJsonResponse, sanitizeTerminalText } from './json-response.js';
|
|
50
56
|
function getErrorMessage(error) {
|
|
51
57
|
return error instanceof Error ? error.message : String(error);
|
|
52
58
|
}
|
|
@@ -176,9 +182,6 @@ function createCodeVerifier() {
|
|
|
176
182
|
export function createCodeChallenge(verifier) {
|
|
177
183
|
return createHash('sha256').update(verifier).digest('base64url');
|
|
178
184
|
}
|
|
179
|
-
export function sanitizeTerminalText(value) {
|
|
180
|
-
return String(value).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, '');
|
|
181
|
-
}
|
|
182
185
|
function printError(message) {
|
|
183
186
|
console.error(sanitizeTerminalText(message));
|
|
184
187
|
}
|
|
@@ -241,45 +244,9 @@ export function formatTerminalLink(url) {
|
|
|
241
244
|
}
|
|
242
245
|
return `\u001B]8;;${safeUrl}\u0007${safeUrl}\u001B]8;;\u0007`;
|
|
243
246
|
}
|
|
244
|
-
export async function parseJsonResponse(response, context) {
|
|
245
|
-
const contentType = response.headers.get('content-type') || '';
|
|
246
|
-
const rawBody = await response.text();
|
|
247
|
-
if (!contentType.includes('application/json')) {
|
|
248
|
-
throw new Error(`${context} returned non-JSON response (status ${response.status}). ` +
|
|
249
|
-
`Body preview: ${sanitizeTerminalText(rawBody.slice(0, 180))}`);
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
return JSON.parse(rawBody);
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
throw new Error(`${context} returned invalid JSON (status ${response.status}). ` +
|
|
256
|
-
`Body preview: ${sanitizeTerminalText(rawBody.slice(0, 180))}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
247
|
function shellQuote(value) {
|
|
260
248
|
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
261
249
|
}
|
|
262
|
-
function printPromptSummaries(items, emptyMessage) {
|
|
263
|
-
if (items.length === 0) {
|
|
264
|
-
printLine(emptyMessage);
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
const rows = items.map(item => ({
|
|
268
|
-
id: sanitizeTerminalText(item.id),
|
|
269
|
-
name: sanitizeTerminalText(item.name),
|
|
270
|
-
role: sanitizeTerminalText(item.role),
|
|
271
|
-
status: sanitizeTerminalText(item.status || 'active'),
|
|
272
|
-
}));
|
|
273
|
-
const idWidth = Math.max('ID'.length, ...rows.map(row => row.id.length));
|
|
274
|
-
const nameWidth = Math.max('NAME'.length, ...rows.map(row => row.name.length));
|
|
275
|
-
const roleWidth = Math.max('ROLE'.length, ...rows.map(row => row.role.length));
|
|
276
|
-
const statusWidth = Math.max('STATUS'.length, ...rows.map(row => row.status.length));
|
|
277
|
-
printLine(`${'ID'.padEnd(idWidth)} ${'NAME'.padEnd(nameWidth)} ${'ROLE'.padEnd(roleWidth)} ${'STATUS'.padEnd(statusWidth)}`);
|
|
278
|
-
printLine(`${'-'.repeat(idWidth)} ${'-'.repeat(nameWidth)} ${'-'.repeat(roleWidth)} ${'-'.repeat(statusWidth)}`);
|
|
279
|
-
rows.forEach(row => {
|
|
280
|
-
printLine(`${row.id.padEnd(idWidth)} ${row.name.padEnd(nameWidth)} ${row.role.padEnd(roleWidth)} ${row.status.padEnd(statusWidth)}`);
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
250
|
function readJsonObjectArg(valueArg, label) {
|
|
284
251
|
if (!valueArg) {
|
|
285
252
|
return {};
|
|
@@ -918,7 +885,7 @@ async function listPrompts() {
|
|
|
918
885
|
printJson(data);
|
|
919
886
|
return;
|
|
920
887
|
}
|
|
921
|
-
|
|
888
|
+
printPromptSummaryTable(data.prompts, 'No prompts found.', printLine);
|
|
922
889
|
}
|
|
923
890
|
async function listJudges() {
|
|
924
891
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
@@ -936,7 +903,7 @@ async function listJudges() {
|
|
|
936
903
|
printJson(data);
|
|
937
904
|
return;
|
|
938
905
|
}
|
|
939
|
-
|
|
906
|
+
printPromptSummaryTable(data.judges, 'No judges found.', printLine);
|
|
940
907
|
}
|
|
941
908
|
async function listScorers() {
|
|
942
909
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
@@ -1014,14 +981,17 @@ async function pushRunnerArtifact(kind) {
|
|
|
1014
981
|
}
|
|
1015
982
|
async function pushPromptArtifact(kind) {
|
|
1016
983
|
const promptDir = getPositionalArg(2);
|
|
1017
|
-
const
|
|
984
|
+
const projectArg = getArg('--project');
|
|
1018
985
|
const runnerVersionArg = getArg('--runner-version');
|
|
1019
986
|
const parentVersionId = getArg('--parent') || undefined;
|
|
1020
987
|
const sessionId = getArg('--session') || undefined;
|
|
1021
|
-
const usage = `Usage: orizu ${kind === 'judge' ? 'judges' : 'prompts'} push <dir> --project <team/project> [--runner-version <id>] [--parent <version-id>] [--session <session-id>] [--json]`;
|
|
1022
|
-
if (!promptDir)
|
|
988
|
+
const usage = `Usage: orizu ${kind === 'judge' ? 'judges' : 'prompts'} push <dir> --project <team/project> [--runner-version <id>] [--parent <version-id>] [--session <session-id>] [--report-file <path>] [--json]`;
|
|
989
|
+
if (!promptDir)
|
|
1023
990
|
throw new Error(usage);
|
|
1024
|
-
|
|
991
|
+
if (cliArgs.some(arg => arg === '--report' || arg.startsWith('--report=')))
|
|
992
|
+
throw new Error(`${kind === 'judge' ? 'judges' : 'prompts'} push accepts report input only through --report-file`);
|
|
993
|
+
const report = readMarkdownReportInput(cliArgs, 'Prompt');
|
|
994
|
+
const project = projectArg || await resolveProjectSlug(null);
|
|
1025
995
|
const promptRoot = expandHomePath(promptDir);
|
|
1026
996
|
const manifest = readJsonFile(join(promptRoot, 'orizu.prompt.json'));
|
|
1027
997
|
const primaryText = readPromptPrimaryText(manifest, promptRoot);
|
|
@@ -1040,6 +1010,7 @@ async function pushPromptArtifact(kind) {
|
|
|
1040
1010
|
kind, project, sessionId, manifest, sidecars, runnerVersionId, parentVersionId,
|
|
1041
1011
|
displayName: String(manifest.name || promptDir),
|
|
1042
1012
|
bodyKind: primaryText.bodyKind, primaryBody: primaryText.body,
|
|
1013
|
+
report,
|
|
1043
1014
|
});
|
|
1044
1015
|
if (hasJsonFlag())
|
|
1045
1016
|
printJson(data);
|
|
@@ -1074,10 +1045,16 @@ async function pushPromptArtifact(kind) {
|
|
|
1074
1045
|
parentVersionId,
|
|
1075
1046
|
versionLabel: manifest.version_label,
|
|
1076
1047
|
createdBy: manifest.provenance || { kind: 'human-edit' },
|
|
1048
|
+
...(report
|
|
1049
|
+
? {
|
|
1050
|
+
reportMarkdown: report.markdown,
|
|
1051
|
+
reportSourceName: report.sourceName,
|
|
1052
|
+
}
|
|
1053
|
+
: {}),
|
|
1077
1054
|
}),
|
|
1078
1055
|
});
|
|
1079
1056
|
if (!response.ok) {
|
|
1080
|
-
throw new Error(
|
|
1057
|
+
throw new Error(await promptPushErrorMessage(response, kind, Boolean(report)));
|
|
1081
1058
|
}
|
|
1082
1059
|
const data = await parseJsonResponse(response, `${kind} push`);
|
|
1083
1060
|
if (hasJsonFlag()) {
|
|
@@ -1658,7 +1635,6 @@ async function execScorer() {
|
|
|
1658
1635
|
printLine(`Wrote scorer result to ${sanitizeTerminalText(outPath)}`);
|
|
1659
1636
|
}
|
|
1660
1637
|
}
|
|
1661
|
-
const MARKDOWN_REPORT_MAX_BYTES = 2 * 1024 * 1024;
|
|
1662
1638
|
function parseOptionalNumberFlag(name) {
|
|
1663
1639
|
const value = getArg(name);
|
|
1664
1640
|
if (!value) {
|
|
@@ -1673,47 +1649,11 @@ function parseOptionalNumberFlag(name) {
|
|
|
1673
1649
|
function hasObjectKeys(value) {
|
|
1674
1650
|
return Object.keys(value).length > 0;
|
|
1675
1651
|
}
|
|
1676
|
-
function readMarkdownReportInput(reportLabel) {
|
|
1677
|
-
const report = getArg('--report');
|
|
1678
|
-
const reportFile = getArg('--report-file');
|
|
1679
|
-
rejectDashPrefixedOptionValue('--report', report);
|
|
1680
|
-
rejectDashPrefixedOptionValue('--report-file', reportFile);
|
|
1681
|
-
if (report && reportFile) {
|
|
1682
|
-
throw new Error('Use either --report or --report-file, not both');
|
|
1683
|
-
}
|
|
1684
|
-
if (!report && !reportFile) {
|
|
1685
|
-
return null;
|
|
1686
|
-
}
|
|
1687
|
-
let markdown;
|
|
1688
|
-
let sourceName;
|
|
1689
|
-
if (reportFile) {
|
|
1690
|
-
const expandedPath = expandHomePath(reportFile);
|
|
1691
|
-
markdown = readSourceFile(reportFile);
|
|
1692
|
-
sourceName = basename(expandedPath);
|
|
1693
|
-
}
|
|
1694
|
-
else if (report?.startsWith('@')) {
|
|
1695
|
-
const path = report.slice(1);
|
|
1696
|
-
const expandedPath = expandHomePath(path);
|
|
1697
|
-
markdown = readSourceFile(path);
|
|
1698
|
-
sourceName = basename(expandedPath);
|
|
1699
|
-
}
|
|
1700
|
-
else {
|
|
1701
|
-
markdown = report || '';
|
|
1702
|
-
sourceName = 'inline';
|
|
1703
|
-
}
|
|
1704
|
-
if (!markdown.trim()) {
|
|
1705
|
-
throw new Error(`${reportLabel} report markdown must not be blank`);
|
|
1706
|
-
}
|
|
1707
|
-
if (Buffer.byteLength(markdown, 'utf8') > MARKDOWN_REPORT_MAX_BYTES) {
|
|
1708
|
-
throw new Error(`${reportLabel} report exceeds ${MARKDOWN_REPORT_MAX_BYTES} bytes`);
|
|
1709
|
-
}
|
|
1710
|
-
return { markdown, sourceName };
|
|
1711
|
-
}
|
|
1712
1652
|
function readOptimizationReportInput() {
|
|
1713
|
-
return readMarkdownReportInput('Optimization');
|
|
1653
|
+
return readMarkdownReportInput(cliArgs, 'Optimization');
|
|
1714
1654
|
}
|
|
1715
1655
|
function readTaskReportInput() {
|
|
1716
|
-
return readMarkdownReportInput('Task');
|
|
1656
|
+
return readMarkdownReportInput(cliArgs, 'Task');
|
|
1717
1657
|
}
|
|
1718
1658
|
async function startOptimizationRun() {
|
|
1719
1659
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
@@ -1853,27 +1793,6 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1853
1793
|
printLine(`Cancelled optimization run ${id}`);
|
|
1854
1794
|
}
|
|
1855
1795
|
}
|
|
1856
|
-
async function exportOptimizationRun() {
|
|
1857
|
-
const runId = getPositionalArg(2) || getArg('--run-id');
|
|
1858
|
-
const outPathArg = getArg('--out');
|
|
1859
|
-
if (!runId) {
|
|
1860
|
-
throw new Error('Usage: orizu optimizations export <run-id> [--out <path>] [--json]');
|
|
1861
|
-
}
|
|
1862
|
-
const response = await authedFetch(`/api/cli/optimization-runs/${encodeURIComponent(runId)}/export`);
|
|
1863
|
-
if (!response.ok) {
|
|
1864
|
-
throw new Error(`Failed to export optimization run: ${await response.text()}`);
|
|
1865
|
-
}
|
|
1866
|
-
const data = await parseJsonResponse(response, 'Optimization export');
|
|
1867
|
-
if (hasJsonFlag() && !outPathArg) {
|
|
1868
|
-
printJson(data);
|
|
1869
|
-
return;
|
|
1870
|
-
}
|
|
1871
|
-
const filename = outPathArg
|
|
1872
|
-
? expandHomePath(outPathArg)
|
|
1873
|
-
: `${runId}.optimization.json`;
|
|
1874
|
-
writeTextFileEnsuringDir(filename, `${JSON.stringify(data, null, 2)}\n`);
|
|
1875
|
-
printLine(`Saved optimization export to ${sanitizeTerminalText(filename)}`);
|
|
1876
|
-
}
|
|
1877
1796
|
// ALI-1073 list moved to optimizations-list-cli.ts (line ratchet, ALI-976);
|
|
1878
1797
|
// ALI-1175 labels its best scores as agent-reported evidence there.
|
|
1879
1798
|
function removeFlagWithValue(args, flag) {
|
|
@@ -4929,6 +4848,13 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
4929
4848
|
await pushPromptArtifact('prompt');
|
|
4930
4849
|
return;
|
|
4931
4850
|
}
|
|
4851
|
+
if (command === 'prompts' && subcommand === 'report' &&
|
|
4852
|
+
(cliArgs[2] === 'set' || cliArgs[2] === 'upload')) {
|
|
4853
|
+
await promptReportCommand(cliArgs, {
|
|
4854
|
+
resolveProjectSlug, json: hasJsonFlag(), printJson, printLine,
|
|
4855
|
+
});
|
|
4856
|
+
return;
|
|
4857
|
+
}
|
|
4932
4858
|
if (command === 'prompts' && subcommand === 'archive') {
|
|
4933
4859
|
await setPromptArchivedState(true);
|
|
4934
4860
|
return;
|
|
@@ -5032,7 +4958,17 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
5032
4958
|
return;
|
|
5033
4959
|
}
|
|
5034
4960
|
if (command === 'optimizations' && subcommand === 'export') {
|
|
5035
|
-
await
|
|
4961
|
+
await exportOptimizationRunCommand({
|
|
4962
|
+
getArg,
|
|
4963
|
+
getPositionalArg,
|
|
4964
|
+
authedFetch,
|
|
4965
|
+
hasJsonFlag,
|
|
4966
|
+
printJson,
|
|
4967
|
+
printLine,
|
|
4968
|
+
expandHomePath,
|
|
4969
|
+
writeTextFileEnsuringDir,
|
|
4970
|
+
sanitizeTerminalText,
|
|
4971
|
+
});
|
|
5036
4972
|
return;
|
|
5037
4973
|
}
|
|
5038
4974
|
if (command === 'optimizations' && subcommand === 'list') {
|