orizu 0.5.26 → 0.5.28
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 +19 -0
- package/dist/artifacts-git-runtime.d.ts +34 -0
- package/dist/artifacts-git-runtime.d.ts.map +1 -0
- package/dist/artifacts-git-runtime.js +312 -0
- package/dist/cloudflare-artifacts-git-remote.d.ts +5 -0
- package/dist/cloudflare-artifacts-git-remote.d.ts.map +1 -0
- package/dist/cloudflare-artifacts-git-remote.js +21 -0
- 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/git-credential.d.ts.map +1 -1
- package/dist/git-credential.js +1 -1
- package/dist/git-recovery-environment.d.ts +1 -0
- package/dist/git-recovery-environment.d.ts.map +1 -1
- package/dist/git-recovery-environment.js +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +44 -11
- package/dist/hosted-boot.d.ts +75 -2
- package/dist/hosted-boot.d.ts.map +1 -1
- package/dist/hosted-boot.js +585 -102
- package/dist/hosted-bootstrap.d.ts +2 -0
- package/dist/hosted-bootstrap.d.ts.map +1 -1
- package/dist/hosted-bootstrap.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 +126 -24
- package/dist/hosted-harness.d.ts +3 -0
- package/dist/hosted-harness.d.ts.map +1 -1
- package/dist/hosted-harness.js +4 -1
- package/dist/hosted-harvest.d.ts +30 -9
- package/dist/hosted-harvest.d.ts.map +1 -1
- package/dist/hosted-harvest.js +222 -28
- package/dist/hosted-loop-lifecycle.d.ts +15 -3
- package/dist/hosted-loop-lifecycle.d.ts.map +1 -1
- package/dist/hosted-loop-lifecycle.js +6 -4
- package/dist/hosted-loop.d.ts +47 -7
- package/dist/hosted-loop.d.ts.map +1 -1
- package/dist/hosted-loop.js +310 -64
- package/dist/hosted-question-limits.d.ts +7 -0
- package/dist/hosted-question-limits.d.ts.map +1 -0
- package/dist/hosted-question-limits.js +6 -0
- package/dist/hosted-question.d.ts +27 -0
- package/dist/hosted-question.d.ts.map +1 -0
- package/dist/hosted-question.js +118 -0
- package/dist/hosted-run-event-sink.d.ts +10 -5
- package/dist/hosted-run-event-sink.d.ts.map +1 -1
- package/dist/hosted-run-event-sink.js +12 -8
- package/dist/hosted-runtime-assets.d.ts +16 -7
- package/dist/hosted-runtime-assets.d.ts.map +1 -1
- package/dist/hosted-runtime-assets.js +40 -12
- package/dist/hosted-session-cli.d.ts.map +1 -1
- package/dist/hosted-session-cli.js +10 -19
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +113 -92
- package/dist/instruction-set-gepa-launch.d.ts +1 -1
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -1
- package/dist/instruction-set-gepa-launch.js +5 -3
- package/dist/instruction-set-loader.d.ts.map +1 -1
- package/dist/instruction-set-loader.js +63 -2
- package/dist/instruction-set-lock/index.d.ts +51 -0
- package/dist/instruction-set-lock/index.d.ts.map +1 -0
- package/dist/instruction-set-lock/index.js +459 -0
- package/dist/instruction-set-lock/schema.v1.d.ts +136 -0
- package/dist/instruction-set-lock/schema.v1.d.ts.map +1 -0
- package/dist/instruction-set-lock/schema.v1.js +155 -0
- package/dist/instruction-set-provenance.d.ts +26 -0
- package/dist/instruction-set-provenance.d.ts.map +1 -0
- package/dist/instruction-set-provenance.js +68 -0
- package/dist/instruction-set-sync/helpers.d.ts +22 -0
- package/dist/instruction-set-sync/helpers.d.ts.map +1 -0
- package/dist/instruction-set-sync/helpers.js +677 -0
- package/dist/instruction-set-sync/index.d.ts +60 -0
- package/dist/instruction-set-sync/index.d.ts.map +1 -0
- package/dist/instruction-set-sync/index.js +689 -0
- package/dist/instruction-set-update/index.d.ts +42 -0
- package/dist/instruction-set-update/index.d.ts.map +1 -0
- package/dist/instruction-set-update/index.js +445 -0
- package/dist/instruction-set-verify/index.d.ts +27 -0
- package/dist/instruction-set-verify/index.d.ts.map +1 -0
- package/dist/instruction-set-verify/index.js +865 -0
- package/dist/instruction-sets-cli.d.ts +1 -4
- package/dist/instruction-sets-cli.d.ts.map +1 -1
- package/dist/instruction-sets-cli.js +220 -109
- package/dist/model-config-fields.d.ts +35 -0
- package/dist/model-config-fields.d.ts.map +1 -0
- package/dist/model-config-fields.js +28 -0
- package/dist/optimizations-list-cli.d.ts.map +1 -1
- package/dist/optimizations-list-cli.js +7 -4
- package/dist/score-format.d.ts +2 -0
- package/dist/score-format.d.ts.map +1 -0
- package/dist/score-format.js +18 -0
- package/dist/workbench-repo-contract.d.ts +65 -0
- package/dist/workbench-repo-contract.d.ts.map +1 -0
- package/dist/workbench-repo-contract.js +250 -0
- package/dist/workspace-bootstrap-job.d.ts +45 -0
- package/dist/workspace-bootstrap-job.d.ts.map +1 -0
- package/dist/workspace-bootstrap-job.js +431 -0
- package/dist/workspace-bootstrap-seed.d.ts +74 -0
- package/dist/workspace-bootstrap-seed.d.ts.map +1 -0
- package/dist/workspace-bootstrap-seed.js +209 -0
- package/package.json +4 -3
- package/src/artifacts-git-runtime.ts +360 -0
- package/src/cloudflare-artifacts-git-remote.ts +24 -0
- 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/git-credential.ts +1 -2
- package/src/git-recovery-environment.ts +1 -1
- package/src/help.ts +44 -11
- package/src/hosted-boot.ts +644 -109
- package/src/hosted-bootstrap.ts +14 -3
- package/src/hosted-harness-opencode.ts +130 -28
- package/src/hosted-harness.ts +4 -1
- package/src/hosted-harvest.ts +258 -33
- package/src/hosted-loop-lifecycle.ts +19 -5
- package/src/hosted-loop.ts +340 -68
- package/src/hosted-question-limits.ts +6 -0
- package/src/hosted-question.ts +145 -0
- package/src/hosted-run-event-sink.ts +24 -12
- package/src/hosted-runtime-assets.ts +51 -11
- package/src/hosted-session-cli.ts +10 -24
- package/src/http.ts +10 -4
- package/src/index.ts +108 -134
- package/src/instruction-set-gepa-launch.ts +4 -4
- package/src/instruction-set-loader.ts +53 -2
- package/src/instruction-set-lock/index.ts +561 -0
- package/src/instruction-set-lock/schema.v1.json +141 -0
- package/src/instruction-set-lock/schema.v1.ts +155 -0
- package/src/instruction-set-provenance.ts +89 -0
- package/src/instruction-set-sync/helpers.ts +707 -0
- package/src/instruction-set-sync/index.ts +791 -0
- package/src/instruction-set-update/index.ts +488 -0
- package/src/instruction-set-verify/index.ts +1067 -0
- package/src/instruction-sets-cli.ts +226 -114
- package/src/model-config-fields.ts +31 -0
- package/src/optimizations-list-cli.ts +7 -4
- package/src/score-format.ts +16 -0
- package/src/workbench-repo-contract.ts +314 -0
- package/src/workspace-bootstrap-job.ts +611 -0
- package/src/workspace-bootstrap-seed.ts +350 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +46 -2
- package/vendor/skills/orizu/SKILL.md +4 -2
- package/vendor/skills/orizu/flows/promote.md +4 -0
- package/vendor/skills/orizu/references/assess-and-plan.md +1 -1
- package/vendor/skills/orizu/references/authority-map.md +1 -1
- package/vendor/skills/orizu/references/cli-reference.md +151 -35
- package/vendor/skills/orizu/references/instructions-after-prompts.md +3 -3
- package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +5 -6
- package/vendor/skills/orizu/references/migrate-hand-rolled-instruction-layout.md +252 -0
- package/vendor/skills/orizu/references/optimization-reports.md +5 -4
- package/vendor/skills/orizu/references/optimization-with-gepa.md +1 -1
- package/vendor/skills/orizu/references/orizu-in-your-codebase.md +195 -0
- package/vendor/skills/orizu/references/primer.md +4 -2
- package/vendor/skills/orizu/references/prompt-control-plane.md +33 -29
- package/vendor/skills/orizu/references/vocabulary.md +1 -1
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { authedFetch } from './http.js'
|
|
2
|
+
import { INSTRUCTION_SET_COMPONENT_KEY } from './instruction-set-lock/index.js'
|
|
2
3
|
import { loadInstructionSetManifest, MAX_INSTRUCTION_SET_COMPONENT_BYTES, type InstructionSetManifest } from './instruction-set-manifest.js'
|
|
3
4
|
import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js'
|
|
5
|
+
import { parseSyncTarget, planSyncRequest, syncPayloadToDisk, type SyncPayload, type SyncTarget } from './instruction-set-sync/index.js'
|
|
6
|
+
import { applyPrune, applyUpdate, lockedProfileIdentity, makeUpdatePlan, planPrune, PruneKeepUnresolvedError, readUpdateLock } from './instruction-set-update/index.js'
|
|
7
|
+
import { printVerifyReport, verifyInstructionSetTree } from './instruction-set-verify/index.js'
|
|
4
8
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs'
|
|
5
9
|
import { join } from 'node:path'
|
|
6
10
|
import { createHash, randomUUID } from 'node:crypto'
|
|
@@ -31,6 +35,45 @@ function argValue(args: string[], flag: string): string | null {
|
|
|
31
35
|
: args[index + 1]!
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
function requiredOptionValue(args: string[], subcommand: string, flag: string): string | null {
|
|
39
|
+
const value = argValue(args, flag)
|
|
40
|
+
if (args.includes(flag) && value === null) {
|
|
41
|
+
throw new Error(`instruction_set_${subcommand}_option_value_missing:${flag}`)
|
|
42
|
+
}
|
|
43
|
+
return value
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function waitAtPrunePlanBarrierForProcessTest(): void {
|
|
47
|
+
const readyPath = process.env.ORIZU_TEST_PRUNE_PLAN_READY_PATH
|
|
48
|
+
const releasePath = process.env.ORIZU_TEST_PRUNE_PLAN_RELEASE_PATH
|
|
49
|
+
if (readyPath === undefined && releasePath === undefined) return
|
|
50
|
+
if (!readyPath || !releasePath) throw new Error('instruction_set_prune_test_barrier_invalid')
|
|
51
|
+
writeFileSync(readyPath, `${process.pid}\n`, { flag: 'wx' })
|
|
52
|
+
const deadline = Date.now() + 10_000
|
|
53
|
+
while (!existsSync(releasePath)) {
|
|
54
|
+
if (Date.now() >= deadline) throw new Error('instruction_set_prune_test_barrier_timeout')
|
|
55
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function unknownPruneOption(args: string[]): string | undefined {
|
|
60
|
+
const allowed = new Set(['--out', '--keep', '--yes', '--json'])
|
|
61
|
+
return args.find(value => value.startsWith('--') && !allowed.has(value))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function lifecycleOptionValues(args: string[], command: 'update' | 'prune', flag: string): string[] {
|
|
65
|
+
const values: string[] = []
|
|
66
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
67
|
+
if (args[index] !== flag) continue
|
|
68
|
+
const value = args[index + 1]
|
|
69
|
+
if (!value || value.startsWith('--')) {
|
|
70
|
+
throw new Error(`instruction_set_${command}_option_missing_value:${flag}`)
|
|
71
|
+
}
|
|
72
|
+
values.push(value)
|
|
73
|
+
}
|
|
74
|
+
return values
|
|
75
|
+
}
|
|
76
|
+
|
|
34
77
|
async function responsePayload(response: Response, action: string): Promise<Record<string, unknown>> {
|
|
35
78
|
const payload = await response.json().catch(() => ({})) as Record<string, unknown>
|
|
36
79
|
if (!response.ok) {
|
|
@@ -40,23 +83,31 @@ async function responsePayload(response: Response, action: string): Promise<Reco
|
|
|
40
83
|
}
|
|
41
84
|
|
|
42
85
|
const SAFE_SEGMENT = /^[A-Za-z0-9._-]+$/u
|
|
86
|
+
const WINDOWS_RESERVED_COMPONENT_KEY = /^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/iu
|
|
87
|
+
|
|
43
88
|
export interface SyncComponent { body?: string; repoPath?: string; contentSha?: string; commitSha?: string }
|
|
44
89
|
export interface SyncMaterial { profileVersionId: string; versionNumber: number; modelConfigIdentity: string; resolvedFrom: string; components: Record<string, SyncComponent> }
|
|
45
90
|
export interface SyncProfile { modelConfigIdentity: string; resolvedFrom: string; production: SyncMaterial | null }
|
|
46
91
|
export interface SyncSet {
|
|
47
|
-
projectId?: string
|
|
48
|
-
instructionSetId?: string
|
|
49
92
|
name: string
|
|
50
|
-
slug?: string
|
|
51
93
|
description?: string | null
|
|
52
94
|
shape: string[]
|
|
53
95
|
default: SyncMaterial
|
|
54
96
|
profiles: SyncProfile[]
|
|
55
97
|
filteredTo?: string[]
|
|
56
98
|
}
|
|
57
|
-
function safeSegment(value: string) {
|
|
58
|
-
|
|
59
|
-
|
|
99
|
+
function safeSegment(value: string) {
|
|
100
|
+
if (!SAFE_SEGMENT.test(value) || value === '.' || value === '..' || value.startsWith('.')) {
|
|
101
|
+
throw new Error('instruction_set_path_unsafe')
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function safeComponentKey(value: string, platform: NodeJS.Platform) {
|
|
105
|
+
if (
|
|
106
|
+
!INSTRUCTION_SET_COMPONENT_KEY.test(value) ||
|
|
107
|
+
(platform === 'win32' && WINDOWS_RESERVED_COMPONENT_KEY.test(value))
|
|
108
|
+
) {
|
|
109
|
+
throw new Error('instruction_set_path_unsafe')
|
|
110
|
+
}
|
|
60
111
|
}
|
|
61
112
|
function slug(identity: string) { return identity.replaceAll('/', '__').replace(/[^A-Za-z0-9._-]/gu, '_') }
|
|
62
113
|
function stable(value: unknown): string { return `${JSON.stringify(value, null, 2)}\n` }
|
|
@@ -68,12 +119,12 @@ export interface SyncFileOps {
|
|
|
68
119
|
rmSync?: typeof rmSync
|
|
69
120
|
}
|
|
70
121
|
|
|
71
|
-
function writeMaterial(root: string, material: SyncMaterial, shape: string[], manifestRoot: string, fileOps: SyncFileOps = { writeFileSync }) {
|
|
72
|
-
const files: Record<string, string> =
|
|
73
|
-
const syncedContentSha256: Record<string, string> =
|
|
74
|
-
const pinnedComponents: Record<string, unknown> =
|
|
122
|
+
function writeMaterial(root: string, material: SyncMaterial, shape: string[], manifestRoot: string, platform: NodeJS.Platform, fileOps: SyncFileOps = { writeFileSync }) {
|
|
123
|
+
const files: Record<string, string> = Object.create(null)
|
|
124
|
+
const syncedContentSha256: Record<string, string> = Object.create(null)
|
|
125
|
+
const pinnedComponents: Record<string, unknown> = Object.create(null)
|
|
75
126
|
for (const key of [...shape].sort()) {
|
|
76
|
-
|
|
127
|
+
safeComponentKey(key, platform)
|
|
77
128
|
const component = material.components[key]
|
|
78
129
|
if (typeof component?.body === 'string') {
|
|
79
130
|
const relative = `${manifestRoot}/${key}.md`
|
|
@@ -94,49 +145,14 @@ function removeOwnScratch(paths: Array<string | null>) {
|
|
|
94
145
|
for (const path of paths) if (path && existsSync(path)) rmSync(path, { recursive: true, force: true })
|
|
95
146
|
}
|
|
96
147
|
|
|
97
|
-
function existingDestinationIsSynced(
|
|
98
|
-
destination: string,
|
|
99
|
-
set: Pick<SyncSet, 'projectId' | 'instructionSetId' | 'name' | 'slug'>
|
|
100
|
-
) {
|
|
101
|
-
if (
|
|
102
|
-
set.slug &&
|
|
103
|
-
set.projectId === undefined &&
|
|
104
|
-
set.instructionSetId === undefined
|
|
105
|
-
) {
|
|
106
|
-
return existingDestinationIsLegacySynced(destination, set.name, set.slug)
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
if (!statSync(destination).isDirectory()) return false
|
|
110
|
-
const manifest = JSON.parse(readFileSync(join(destination, 'manifest.json'), 'utf8')) as {
|
|
111
|
-
manifestVersion?: number
|
|
112
|
-
projectId?: string
|
|
113
|
-
instructionSetId?: string
|
|
114
|
-
name?: string
|
|
115
|
-
slug?: string
|
|
116
|
-
}
|
|
117
|
-
return manifest.manifestVersion === 1 && (set.slug
|
|
118
|
-
? manifest.slug === set.slug &&
|
|
119
|
-
manifest.projectId === set.projectId &&
|
|
120
|
-
manifest.instructionSetId === set.instructionSetId
|
|
121
|
-
: manifest.name === set.name)
|
|
122
|
-
} catch { return false }
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function existingDestinationIsLegacySynced(
|
|
126
|
-
destination: string,
|
|
127
|
-
name: string,
|
|
128
|
-
slug?: string
|
|
129
|
-
) {
|
|
148
|
+
function existingDestinationIsSynced(destination: string, set: Pick<SyncSet, 'name'>) {
|
|
130
149
|
try {
|
|
131
150
|
if (!statSync(destination).isDirectory()) return false
|
|
132
151
|
const manifest = JSON.parse(readFileSync(join(destination, 'manifest.json'), 'utf8')) as {
|
|
133
152
|
manifestVersion?: number
|
|
134
153
|
name?: string
|
|
135
|
-
slug?: string
|
|
136
154
|
}
|
|
137
|
-
return manifest.manifestVersion === 1 &&
|
|
138
|
-
manifest.name === name &&
|
|
139
|
-
manifest.slug === slug
|
|
155
|
+
return manifest.manifestVersion === 1 && manifest.name === set.name
|
|
140
156
|
} catch { return false }
|
|
141
157
|
}
|
|
142
158
|
|
|
@@ -156,39 +172,19 @@ function removePublishedBackups(
|
|
|
156
172
|
if (firstError) throw new Error('instruction_set_sync_cleanup_failed')
|
|
157
173
|
}
|
|
158
174
|
|
|
159
|
-
export function syncToDisk(
|
|
160
|
-
|
|
175
|
+
export function syncToDisk(
|
|
176
|
+
out: string,
|
|
177
|
+
set: SyncSet,
|
|
178
|
+
fileOps: SyncFileOps = { writeFileSync },
|
|
179
|
+
platform: NodeJS.Platform = process.platform
|
|
180
|
+
) {
|
|
181
|
+
const directoryName = set.name
|
|
161
182
|
safeSegment(directoryName)
|
|
162
|
-
const hasProjectId = set.projectId !== undefined
|
|
163
|
-
const hasInstructionSetId = set.instructionSetId !== undefined
|
|
164
|
-
if (set.slug && hasProjectId !== hasInstructionSetId) {
|
|
165
|
-
throw new Error('instruction_set_sync_identity_missing')
|
|
166
|
-
}
|
|
167
183
|
for (const key of set.shape) safeSegment(key)
|
|
168
|
-
const
|
|
169
|
-
for (const profile of set.profiles) {
|
|
170
|
-
const value = slug(profile.modelConfigIdentity)
|
|
171
|
-
safeSegment(value)
|
|
172
|
-
if (profileSlugs.has(value)) throw new Error('instruction_set_slug_collision')
|
|
173
|
-
profileSlugs.add(value)
|
|
174
|
-
}
|
|
184
|
+
for (const profile of set.profiles) safeSegment(slug(profile.modelConfigIdentity))
|
|
175
185
|
mkdirSync(out, { recursive: true })
|
|
176
186
|
const destination = join(out, directoryName)
|
|
177
|
-
|
|
178
|
-
? join(out, set.name)
|
|
179
|
-
: null
|
|
180
|
-
if (existsSync(destination)) {
|
|
181
|
-
if (set.slug && existingDestinationIsLegacySynced(destination, set.name)) {
|
|
182
|
-
throw new Error('instruction_set_sync_legacy_identity_required')
|
|
183
|
-
}
|
|
184
|
-
if (!existingDestinationIsSynced(destination, set)) {
|
|
185
|
-
throw new Error('instruction_set_sync_destination_not_synced')
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
if (legacyDestination && existsSync(legacyDestination)) {
|
|
189
|
-
if (existingDestinationIsLegacySynced(legacyDestination, set.name)) {
|
|
190
|
-
throw new Error('instruction_set_sync_legacy_identity_required')
|
|
191
|
-
}
|
|
187
|
+
if (existsSync(destination) && !existingDestinationIsSynced(destination, set)) {
|
|
192
188
|
throw new Error('instruction_set_sync_destination_not_synced')
|
|
193
189
|
}
|
|
194
190
|
const priorDestinations = [destination]
|
|
@@ -197,10 +193,24 @@ export function syncToDisk(out: string, set: SyncSet, fileOps: SyncFileOps = { w
|
|
|
197
193
|
const backups: Array<{ original: string; backup: string }> = []
|
|
198
194
|
mkdirSync(temp)
|
|
199
195
|
try {
|
|
200
|
-
const defaultMaterial = writeMaterial(
|
|
196
|
+
const defaultMaterial = writeMaterial(
|
|
197
|
+
temp,
|
|
198
|
+
set.default,
|
|
199
|
+
set.shape,
|
|
200
|
+
'default',
|
|
201
|
+
platform,
|
|
202
|
+
fileOps
|
|
203
|
+
)
|
|
201
204
|
const profiles = set.profiles.map(profile => ({
|
|
202
205
|
modelConfigIdentity: profile.modelConfigIdentity, slug: slug(profile.modelConfigIdentity), resolvedFrom: profile.resolvedFrom,
|
|
203
|
-
production: profile.production ? writeMaterial(
|
|
206
|
+
production: profile.production ? writeMaterial(
|
|
207
|
+
temp,
|
|
208
|
+
profile.production,
|
|
209
|
+
set.shape,
|
|
210
|
+
`profiles/${slug(profile.modelConfigIdentity)}`,
|
|
211
|
+
platform,
|
|
212
|
+
fileOps
|
|
213
|
+
) : null,
|
|
204
214
|
})).sort((a, b) => a.modelConfigIdentity.localeCompare(b.modelConfigIdentity))
|
|
205
215
|
const components = [
|
|
206
216
|
...Object.entries(defaultMaterial.files).map(([key, path]) => ({ key, path, syncedContentSha256: defaultMaterial.syncedContentSha256[key] })),
|
|
@@ -210,10 +220,7 @@ export function syncToDisk(out: string, set: SyncSet, fileOps: SyncFileOps = { w
|
|
|
210
220
|
]
|
|
211
221
|
const manifest = {
|
|
212
222
|
manifestVersion: 1,
|
|
213
|
-
...(set.projectId ? { projectId: set.projectId } : {}),
|
|
214
|
-
...(set.instructionSetId ? { instructionSetId: set.instructionSetId } : {}),
|
|
215
223
|
name: set.name,
|
|
216
|
-
...(set.slug ? { slug: set.slug } : {}),
|
|
217
224
|
...(Object.hasOwn(set, 'description') ? { description: set.description ?? null } : {}),
|
|
218
225
|
shape: set.shape,
|
|
219
226
|
components,
|
|
@@ -247,9 +254,68 @@ export function syncToDisk(out: string, set: SyncSet, fileOps: SyncFileOps = { w
|
|
|
247
254
|
export async function instructionSetsCommand(args: string[], io: InstructionSetsCommandIo): Promise<number> {
|
|
248
255
|
const positionals = positionalArgs(args)
|
|
249
256
|
const [subcommand, reference] = positionals
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
257
|
+
const knownSubcommands = new Set([
|
|
258
|
+
'archive', 'create', 'default', 'list', 'profiles', 'prune', 'push',
|
|
259
|
+
'restore', 'scorers', 'shape', 'show', 'sync', 'update', 'verify',
|
|
260
|
+
])
|
|
261
|
+
if (!subcommand || !knownSubcommands.has(subcommand)) {
|
|
262
|
+
throw new Error('Usage: orizu instructions list|show|sync|update|prune|verify|archive|restore')
|
|
263
|
+
}
|
|
264
|
+
if (subcommand === 'verify') {
|
|
265
|
+
const report = await verifyInstructionSetTree(requiredOptionValue(args, subcommand, '--out') ?? '.')
|
|
266
|
+
printVerifyReport(report, io.json, io.print)
|
|
267
|
+
return report.ok ? 0 : 1
|
|
268
|
+
}
|
|
269
|
+
if (subcommand === 'prune') {
|
|
270
|
+
const unknownOption = unknownPruneOption(args)
|
|
271
|
+
if (unknownOption) {
|
|
272
|
+
const code = `instruction_set_prune_option_unknown:${unknownOption}`
|
|
273
|
+
if (!io.json) throw new Error(code)
|
|
274
|
+
io.print(JSON.stringify({ removals: [], applied: false, error: code }))
|
|
275
|
+
return 1
|
|
276
|
+
}
|
|
277
|
+
const output = lifecycleOptionValues(args, 'prune', '--out')[0] ?? '.'
|
|
278
|
+
const keepSpecifiers = lifecycleOptionValues(args, 'prune', '--keep')
|
|
279
|
+
let plan: Awaited<ReturnType<typeof planPrune>>
|
|
280
|
+
try {
|
|
281
|
+
plan = await planPrune(output, keepSpecifiers)
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (!(io.json && error instanceof PruneKeepUnresolvedError)) throw error
|
|
284
|
+
io.print(JSON.stringify({ removals: [], applied: false, error: error.code, unresolved: error.unresolved }))
|
|
285
|
+
return 1
|
|
286
|
+
}
|
|
287
|
+
const isApproved = args.includes('--yes')
|
|
288
|
+
if (!isApproved) {
|
|
289
|
+
if (io.json) io.print(JSON.stringify({ removals: plan.removals, skipped: [], applied: false }))
|
|
290
|
+
else {
|
|
291
|
+
for (const removal of plan.removals) io.print(`Remove ${removal.key}: ${removal.reason}`)
|
|
292
|
+
io.print('Plan only; pass --yes to prune.')
|
|
293
|
+
}
|
|
294
|
+
return 0
|
|
295
|
+
}
|
|
296
|
+
waitAtPrunePlanBarrierForProcessTest()
|
|
297
|
+
try {
|
|
298
|
+
const result = await applyPrune(output, plan, keepSpecifiers)
|
|
299
|
+
if (io.json) io.print(JSON.stringify({ removals: plan.removals, skipped: result.skipped, applied: true }))
|
|
300
|
+
else {
|
|
301
|
+
for (const removal of plan.removals) io.print(`Remove ${removal.key}: ${removal.reason}`)
|
|
302
|
+
for (const removal of result.skipped) {
|
|
303
|
+
const reason = removal.reason === 'profile_directory_has_unmanaged_files'
|
|
304
|
+
? `profile directory contains unmanaged files (${removal.unmanagedEntries!.join(', ')})`
|
|
305
|
+
: 'became referenced after planning'
|
|
306
|
+
io.print(`Skipped ${removal.key}: ${reason}`)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return 0
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (!io.json) throw error
|
|
312
|
+
const code = error instanceof Error ? error.message : String(error)
|
|
313
|
+
io.print(JSON.stringify({ removals: plan.removals, applied: false, error: code }))
|
|
314
|
+
return 1
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (subcommand === 'default' && reference === 'move' && args.some(arg => arg === '--version' || arg.startsWith('--version='))) {
|
|
318
|
+
throw new Error('--version is not valid for default move; use instructions profiles promote')
|
|
253
319
|
}
|
|
254
320
|
let writeManifest: InstructionSetManifest | undefined
|
|
255
321
|
if (subcommand === 'create' || subcommand === 'push') {
|
|
@@ -258,7 +324,48 @@ export async function instructionSetsCommand(args: string[], io: InstructionSets
|
|
|
258
324
|
: 'Usage: orizu instructions push <manifest> --project <team/project> [--set <slug-or-exact-name>] [--runner-version <id>] [--json]')
|
|
259
325
|
writeManifest = loadInstructionSetManifest(reference)
|
|
260
326
|
}
|
|
261
|
-
|
|
327
|
+
let syncTarget: SyncTarget | undefined
|
|
328
|
+
if (subcommand === 'sync') {
|
|
329
|
+
for (const option of ['--project', '--out', '--version', '--target']) {
|
|
330
|
+
if (args.filter(value => value === option).length > 1) {
|
|
331
|
+
throw new Error(`instruction_set_sync_option_duplicate:${option}`)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const suppliedTarget = argValue(args, '--target')
|
|
335
|
+
syncTarget = parseSyncTarget(args.includes('--target') ? suppliedTarget ?? '' : undefined)
|
|
336
|
+
}
|
|
337
|
+
const projectOption = subcommand === 'update'
|
|
338
|
+
? lifecycleOptionValues(args, 'update', '--project')[0] ?? null
|
|
339
|
+
: requiredOptionValue(args, subcommand, '--project')
|
|
340
|
+
const project = await io.resolveProjectSlug(projectOption)
|
|
341
|
+
if (subcommand === 'update') {
|
|
342
|
+
const output = lifecycleOptionValues(args, 'update', '--out')[0] ?? '.'
|
|
343
|
+
const lock = readUpdateLock(output, project)
|
|
344
|
+
const payloads: SyncPayload[] = []
|
|
345
|
+
for (const [setSlug, set] of Object.entries(lock.instructionSets)) {
|
|
346
|
+
const barePath = `/api/cli/instruction-sets/${encodeURIComponent(setSlug)}/sync?project=${encodeURIComponent(project)}`
|
|
347
|
+
payloads.push(await responsePayload(await authedFetch(barePath, { method: 'GET' }), 'Instruction sets update') as unknown as SyncPayload)
|
|
348
|
+
for (const profileSlugValue of Object.keys(set.profiles)) {
|
|
349
|
+
const identity = lockedProfileIdentity(output, setSlug, profileSlugValue)
|
|
350
|
+
const query = new URLSearchParams({ project, profile: identity })
|
|
351
|
+
const path = `/api/cli/instruction-sets/${encodeURIComponent(setSlug)}/sync?${query.toString()}`
|
|
352
|
+
payloads.push(await responsePayload(await authedFetch(path, { method: 'GET' }), 'Instruction sets update') as unknown as SyncPayload)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const plan = makeUpdatePlan(project, lock, payloads)
|
|
356
|
+
const isApproved = args.includes('--yes')
|
|
357
|
+
const result = isApproved
|
|
358
|
+
? await applyUpdate(output, project, plan, args.includes('--no-sync'))
|
|
359
|
+
: { absent: [], warnings: [] }
|
|
360
|
+
if (io.json) io.print(JSON.stringify({ plan: plan.lines, applied: isApproved, absent: result.absent, warnings: result.warnings }))
|
|
361
|
+
else {
|
|
362
|
+
for (const line of plan.lines) io.print(line)
|
|
363
|
+
for (const specifier of result.absent) io.print(`Referenced but absent: ${specifier}`)
|
|
364
|
+
for (const warning of result.warnings) io.print(warning)
|
|
365
|
+
if (!isApproved) io.print('Plan only; pass --yes to update.')
|
|
366
|
+
}
|
|
367
|
+
return 0
|
|
368
|
+
}
|
|
262
369
|
if (subcommand === 'scorers') {
|
|
263
370
|
const action = reference
|
|
264
371
|
const set = positionals[2]
|
|
@@ -298,22 +405,20 @@ export async function instructionSetsCommand(args: string[], io: InstructionSets
|
|
|
298
405
|
return 0
|
|
299
406
|
}
|
|
300
407
|
if (subcommand === 'default') {
|
|
301
|
-
const operation = reference; const set = positionals[2]; const identity = argValue(args, '--model-config')
|
|
302
|
-
if ((operation !== 'show' && operation !== 'move') || !set) throw new Error('Usage: orizu instructions default show|move <set> --project <team/project> [--model-config <identity>
|
|
303
|
-
if (operation === 'move' &&
|
|
408
|
+
const operation = reference; const set = positionals[2]; const identity = argValue(args, '--model-config')
|
|
409
|
+
if ((operation !== 'show' && operation !== 'move') || !set) throw new Error('Usage: orizu instructions default show|move <set> --project <team/project> [--model-config <identity>] [--json]')
|
|
410
|
+
if (operation === 'move' && !identity) throw new Error('--model-config is required')
|
|
304
411
|
const path = `/api/cli/instruction-sets/${encodeURIComponent(set)}/default?project=${encodeURIComponent(project)}`
|
|
305
412
|
if (operation === 'show') {
|
|
306
413
|
const payload = await responsePayload(await authedFetch(path, { method: 'GET' }), 'Instruction sets default show')
|
|
307
|
-
|
|
414
|
+
const current = payload.default as { modelConfigIdentity?: string; production?: { versionNumber?: number } | null } | null
|
|
415
|
+
if (io.json) io.print(JSON.stringify(payload))
|
|
416
|
+
else if (!current?.modelConfigIdentity) io.print('Default: unset')
|
|
417
|
+
else io.print(`Default: ${sanitizeTerminalText(current.modelConfigIdentity)} (${current.production ? `production v${current.production.versionNumber ?? '?'}` : 'not promoted'})`)
|
|
308
418
|
return 0
|
|
309
419
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const resolvesToDefault = Array.isArray(impact.resolvesToDefault) ? impact.resolvesToDefault.filter((item): item is string => typeof item === 'string') : []
|
|
313
|
-
io.print(`These model configs resolve to the default: ${resolvesToDefault.join(', ')}`)
|
|
314
|
-
}
|
|
315
|
-
const payload = await responsePayload(await authedFetch(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ modelConfigIdentity: identity, versionNumber: Number(version) }) }), 'Instruction sets default move')
|
|
316
|
-
if (io.json) io.print(JSON.stringify(payload)); else io.print(`Default moved to ${identity} v${version}`)
|
|
420
|
+
const payload = await responsePayload(await authedFetch(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ modelConfigIdentity: identity }) }), 'Instruction sets default move')
|
|
421
|
+
if (io.json) io.print(JSON.stringify(payload)); else io.print(`Default moved to ${sanitizeTerminalText(identity!)}`)
|
|
317
422
|
return 0
|
|
318
423
|
}
|
|
319
424
|
if (subcommand === 'shape') {
|
|
@@ -337,10 +442,9 @@ export async function instructionSetsCommand(args: string[], io: InstructionSets
|
|
|
337
442
|
if (identities.length) io.print(`${set} does not resolve for these model configs until the pointers move: ${identities.join(', ')}`)
|
|
338
443
|
for (const pointer of affected) if (pointer && typeof pointer === 'object') {
|
|
339
444
|
const item = pointer as { modelConfigIdentity?: unknown; pointer?: unknown; stalePointerVersionNumber?: unknown; headVersionNumber?: unknown; branchedFromVersionNumber?: unknown }
|
|
340
|
-
if (typeof item.modelConfigIdentity === 'string' &&
|
|
341
|
-
const command =
|
|
342
|
-
|
|
343
|
-
if (typeof item.stalePointerVersionNumber === 'number' && typeof item.branchedFromVersionNumber === 'number' && item.branchedFromVersionNumber !== item.stalePointerVersionNumber) io.print(`${operatorAction} would move ${item.pointer} from v${item.stalePointerVersionNumber}'s text to v${item.headVersionNumber} = v${item.branchedFromVersionNumber}'s text + ${key}`)
|
|
445
|
+
if (typeof item.modelConfigIdentity === 'string' && item.pointer === 'production' && typeof item.headVersionNumber === 'number') {
|
|
446
|
+
const command = 'profiles promote'
|
|
447
|
+
if (typeof item.stalePointerVersionNumber === 'number' && typeof item.branchedFromVersionNumber === 'number' && item.branchedFromVersionNumber !== item.stalePointerVersionNumber) io.print(`promote would move ${item.pointer} from v${item.stalePointerVersionNumber}'s text to v${item.headVersionNumber} = v${item.branchedFromVersionNumber}'s text + ${key}`)
|
|
344
448
|
io.print(`Follow up: instructions ${command} ${set} --project ${project} --model-config ${item.modelConfigIdentity} --version ${item.headVersionNumber}`)
|
|
345
449
|
}
|
|
346
450
|
}
|
|
@@ -413,19 +517,27 @@ export async function instructionSetsCommand(args: string[], io: InstructionSets
|
|
|
413
517
|
return 0
|
|
414
518
|
}
|
|
415
519
|
const status = argValue(args, '--status') || 'active'
|
|
416
|
-
if (subcommand !== 'list' && subcommand !== 'show' && subcommand !== 'sync') throw new Error('Usage: orizu instructions list|show|sync|archive|restore')
|
|
417
520
|
if (subcommand === 'sync') {
|
|
418
|
-
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const
|
|
423
|
-
const
|
|
424
|
-
if (
|
|
425
|
-
|
|
521
|
+
if (!reference) throw new Error('Usage: orizu instructions sync <specifier> [--version <n>] [--out <app-root>] [--target <ts>] [--force-helpers] --project <team/project> [--json]')
|
|
522
|
+
if (args.includes('--model-config')) {
|
|
523
|
+
throw new Error('instruction_set_sync_option_retired:--model-config; use <set>/<profile> (see docs/cli.md#specifiers)')
|
|
524
|
+
}
|
|
525
|
+
const allowedOptions = new Set(['--project', '--out', '--version', '--target', '--json', '--force-helpers'])
|
|
526
|
+
const unknownOption = args.find(value => value.startsWith('--') && !allowedOptions.has(value))
|
|
527
|
+
if (unknownOption) throw new Error(`instruction_set_sync_option_unknown:${unknownOption}`)
|
|
528
|
+
const output = requiredOptionValue(args, subcommand, '--out') ?? '.'
|
|
529
|
+
const plan = planSyncRequest(reference, requiredOptionValue(args, subcommand, '--version'), output, project)
|
|
530
|
+
const payload = await responsePayload(await authedFetch(plan.path, { method: 'GET' }), 'Instruction sets sync') as unknown as SyncPayload
|
|
531
|
+
const result = syncPayloadToDisk(output, project, plan, payload, {
|
|
532
|
+
forceHelpers: args.includes('--force-helpers'),
|
|
533
|
+
target: syncTarget!,
|
|
534
|
+
})
|
|
535
|
+
if (io.json) io.print(JSON.stringify({ ...payload, warnings: result.warnings }))
|
|
536
|
+
else {
|
|
537
|
+
io.print(`Synced ${reference} to ${result.destination}`)
|
|
538
|
+
for (const warning of result.warnings) io.print(`Warning: ${warning}`)
|
|
539
|
+
if (plan.usedRecordedPointer) io.print('Using recorded Pointer values; run orizu instructions update to re-resolve them.')
|
|
426
540
|
}
|
|
427
|
-
const destination = syncToDisk(output, set)
|
|
428
|
-
if (io.json) io.print(JSON.stringify(payload)); else io.print(`Synced ${set.name} to ${destination}`)
|
|
429
541
|
return 0
|
|
430
542
|
}
|
|
431
543
|
if (subcommand === 'show' && (!reference || reference.startsWith('--'))) throw new Error('Instruction set name is required')
|
|
@@ -449,10 +561,10 @@ export async function instructionSetsCommand(args: string[], io: InstructionSets
|
|
|
449
561
|
}
|
|
450
562
|
return 0
|
|
451
563
|
}
|
|
452
|
-
const set = payload.instructionSet as { name?: string; description?: string | null; shape?: string[]; status?: string; default?: {
|
|
564
|
+
const set = payload.instructionSet as { name?: string; description?: string | null; shape?: string[]; status?: string; default?: { modelConfigIdentity?: string } | null; profiles?: Array<{ modelConfigIdentity?: string | null; production?: { versionNumber?: number } | null; latestVersionNumber?: number | null }> } | undefined
|
|
453
565
|
io.print(`${set?.name || reference}${set?.status === 'archived' ? ' [archived]' : ''}: ${set?.shape?.join(', ') || ''}`)
|
|
454
566
|
if (set?.description) io.print(`Description: ${sanitizeHumanInlineText(sanitizeTerminalText, set.description)}`)
|
|
455
|
-
if (set?.default) io.print(`Default:
|
|
567
|
+
if (set?.default?.modelConfigIdentity) io.print(`Default: ${sanitizeTerminalText(set.default.modelConfigIdentity)}`)
|
|
456
568
|
for (const profile of set?.profiles || []) {
|
|
457
569
|
io.print(`${profile.modelConfigIdentity || 'unspecified'}: production ${profile.production ? `v${profile.production.versionNumber}` : '—'}, latest ${profile.latestVersionNumber ?? '—'}`)
|
|
458
570
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const MODEL_CONFIG_FIELDS = [
|
|
2
|
+
// The pilot's frozen settings carry the provider model name under `model`.
|
|
3
|
+
{ field: 'MODEL', settingsKeys: ['model'], kind: 'string' },
|
|
4
|
+
// `protocol` names the transport/API protocol selected for a customer model call.
|
|
5
|
+
{ field: 'PROTOCOL', settingsKeys: ['protocol'], kind: 'string' },
|
|
6
|
+
// The pilot uses `reasoning_effort`; hosted settings accept OpenAI `reasoning.effort` and Anthropic `output_config.effort`.
|
|
7
|
+
{ field: 'THINKING_LEVEL', settingsKeys: ['reasoning_effort', 'reasoning.effort', 'output_config.effort'], kind: 'string' },
|
|
8
|
+
// Responses uses `max_output_tokens`; Messages uses `max_tokens` for the output limit.
|
|
9
|
+
{ field: 'MAX_OUTPUT_TOKENS', settingsKeys: ['max_output_tokens', 'max_tokens'], kind: 'number' },
|
|
10
|
+
// `temperature` feeds the temperature call option for both supported providers.
|
|
11
|
+
{ field: 'TEMPERATURE', settingsKeys: ['temperature'], kind: 'number' },
|
|
12
|
+
// `top_p` feeds the top-p call option accepted by both supported providers.
|
|
13
|
+
{ field: 'TOP_P', settingsKeys: ['top_p'], kind: 'number' },
|
|
14
|
+
// `strict_json_schema` controls strict schema enforcement in a structured-output call.
|
|
15
|
+
{ field: 'STRICT_JSON_SCHEMA', settingsKeys: ['strict_json_schema'], kind: 'boolean' },
|
|
16
|
+
] as const
|
|
17
|
+
|
|
18
|
+
export type ModelConfigField = typeof MODEL_CONFIG_FIELDS[number]['field']
|
|
19
|
+
|
|
20
|
+
// Dotted settings keys resolve exactly one object level; they are not a general path language.
|
|
21
|
+
export const PROVIDER_REQUEST_FIELDS: Record<ModelConfigField, { openai: string | null; anthropic: string | null }> = {
|
|
22
|
+
MODEL: { openai: 'model', anthropic: 'model' },
|
|
23
|
+
// Orizu consumes protocol selection; it is not a provider request field.
|
|
24
|
+
PROTOCOL: { openai: null, anthropic: null },
|
|
25
|
+
THINKING_LEVEL: { openai: 'reasoning.effort', anthropic: 'output_config.effort' },
|
|
26
|
+
MAX_OUTPUT_TOKENS: { openai: 'max_output_tokens', anthropic: 'max_tokens' },
|
|
27
|
+
TEMPERATURE: { openai: 'temperature', anthropic: 'temperature' },
|
|
28
|
+
TOP_P: { openai: 'top_p', anthropic: 'top_p' },
|
|
29
|
+
// Orizu consumes strict schema selection; it is not a provider request field.
|
|
30
|
+
STRICT_JSON_SCHEMA: { openai: null, anthropic: null },
|
|
31
|
+
}
|
|
@@ -28,6 +28,7 @@ interface OptimizationRunSummary {
|
|
|
28
28
|
archiveStatus?: string
|
|
29
29
|
archivedAt?: string | null
|
|
30
30
|
optimizerVersionId: string | null
|
|
31
|
+
profileVersionId: string | null
|
|
31
32
|
datasetVersionId: string | null
|
|
32
33
|
bestScore: number | null
|
|
33
34
|
resultPromptVersionId: string | null
|
|
@@ -80,11 +81,13 @@ export async function listOptimizationRunsCommand(args: string[], io: Optimizati
|
|
|
80
81
|
if (!['active', 'archived', 'all'].includes(status)) {
|
|
81
82
|
throw new Error(
|
|
82
83
|
'Usage: orizu optimizations list [--project <team/project>] ' +
|
|
83
|
-
'[--status active|archived|all] [--json]'
|
|
84
|
+
'[--status active|archived|all] [--profile-version <id>] [--json]'
|
|
84
85
|
)
|
|
85
86
|
}
|
|
86
87
|
const params = new URLSearchParams({ project })
|
|
87
88
|
if (status !== 'active') params.set('status', status)
|
|
89
|
+
const profileVersionId = argValue(args, '--profile-version')
|
|
90
|
+
if (profileVersionId) params.set('profileVersionId', profileVersionId)
|
|
88
91
|
const response = await fetcher(`/api/cli/optimization-runs?${params.toString()}`)
|
|
89
92
|
if (!response.ok) {
|
|
90
93
|
throw new Error(
|
|
@@ -121,13 +124,13 @@ export async function listOptimizationRunsCommand(args: string[], io: Optimizati
|
|
|
121
124
|
'ARCHIVE'.length,
|
|
122
125
|
...rows.map(row => String(row.archiveStatus || 'active').length)
|
|
123
126
|
)
|
|
124
|
-
io.print(`${'ID'.padEnd(idWidth)} ${'STATUS'.padEnd(statusWidth)} ${'ARCHIVE'.padEnd(archiveWidth)} BEST PROMOTED_VERSION`)
|
|
125
|
-
io.print(`${'-'.repeat(idWidth)} ${'-'.repeat(statusWidth)} ${'-'.repeat(archiveWidth)} ${'-'.repeat(5)} ${'-'.repeat('PROMOTED_VERSION'.length)}`)
|
|
127
|
+
io.print(`${'ID'.padEnd(idWidth)} ${'STATUS'.padEnd(statusWidth)} ${'ARCHIVE'.padEnd(archiveWidth)} BEST PROFILE_VERSION PROMOTED_VERSION`)
|
|
128
|
+
io.print(`${'-'.repeat(idWidth)} ${'-'.repeat(statusWidth)} ${'-'.repeat(archiveWidth)} ${'-'.repeat(5)} ${'-'.repeat('PROFILE_VERSION'.length)} ${'-'.repeat('PROMOTED_VERSION'.length)}`)
|
|
126
129
|
for (const row of rows) {
|
|
127
130
|
const best = row.bestScore === null || row.bestScore === undefined ? '-' : String(row.bestScore)
|
|
128
131
|
const promoted = row.resultPromptVersionId || '-'
|
|
129
132
|
const archiveStatus = row.archiveStatus || 'active'
|
|
130
|
-
io.print(`${String(row.id).padEnd(idWidth)} ${String(row.status).padEnd(statusWidth)} ${archiveStatus.padEnd(archiveWidth)} ${best.padEnd(5)} ${promoted}`)
|
|
133
|
+
io.print(`${String(row.id).padEnd(idWidth)} ${String(row.status).padEnd(statusWidth)} ${archiveStatus.padEnd(archiveWidth)} ${best.padEnd(5)} ${row.profileVersionId || '-'} ${promoted}`)
|
|
131
134
|
}
|
|
132
135
|
// ALI-1175: label the claim — nothing here is server-attested.
|
|
133
136
|
io.print(BEST_SCORE_PROVENANCE_NOTICE)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const SCORE_FORMATS = new Set(['percent', 'number', 'integer', 'currency'])
|
|
2
|
+
|
|
3
|
+
export function formatScoreForCli(score: unknown, scoreFormat: unknown): string {
|
|
4
|
+
if (typeof scoreFormat !== 'string' || !SCORE_FORMATS.has(scoreFormat)) {
|
|
5
|
+
return String(score ?? '—')
|
|
6
|
+
}
|
|
7
|
+
if (score === null || score === undefined || (typeof score === 'number' && Number.isNaN(score))) {
|
|
8
|
+
return 'Not scored'
|
|
9
|
+
}
|
|
10
|
+
if (typeof score !== 'number') return String(score)
|
|
11
|
+
|
|
12
|
+
if (scoreFormat === 'number') return score.toFixed(3).replace(/\.?0+$/u, '')
|
|
13
|
+
if (scoreFormat === 'integer') return Math.round(score).toLocaleString()
|
|
14
|
+
if (scoreFormat === 'currency') return `$${score.toFixed(2)}`
|
|
15
|
+
return `${(score * 100).toFixed(1)}%`
|
|
16
|
+
}
|