orizu 0.5.7 → 0.5.9
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 +13 -2
- package/dist/credentials.js +44 -0
- package/dist/help.js +77 -7
- package/dist/hosted-bootstrap.js +45 -0
- package/dist/hosted-harness-opencode.js +21 -1
- package/dist/hosted-harness.js +9 -0
- package/dist/hosted-harvest.js +121 -0
- package/dist/hosted-headless.js +163 -0
- package/dist/hosted-loop.js +119 -5
- package/dist/hosted-run-event-sink.js +21 -0
- package/dist/hosted-runtime-assets.js +27 -0
- package/dist/hosted-session-cli.js +51 -9
- package/dist/http.js +16 -1
- package/dist/index.js +54 -86
- package/dist/manifests-cli.js +26 -4
- package/dist/report-comments-cli.js +325 -0
- package/dist/skill-installer.js +16 -1
- package/package.json +1 -1
- package/src/credentials.ts +46 -0
- package/src/help.ts +77 -7
- package/src/hosted-bootstrap.ts +52 -0
- package/src/hosted-harness-opencode.ts +24 -2
- package/src/hosted-harness.ts +9 -0
- package/src/hosted-harvest.ts +164 -0
- package/src/hosted-headless.ts +178 -0
- package/src/hosted-loop.ts +133 -4
- package/src/hosted-run-event-sink.ts +28 -0
- package/src/hosted-runtime-assets.ts +31 -0
- package/src/hosted-session-cli.ts +50 -9
- package/src/http.ts +23 -1
- package/src/index.ts +74 -156
- package/src/manifests-cli.ts +28 -4
- package/src/report-comments-cli.ts +524 -0
- package/src/skill-installer.ts +18 -1
- package/vendor/skills/orizu-cli/SKILL.md +9 -2
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +1 -1
|
@@ -359,6 +359,12 @@ async function defaultResolveRepo(
|
|
|
359
359
|
export async function startHostedSession(
|
|
360
360
|
opts: StartHostedSessionOptions
|
|
361
361
|
): Promise<StartHostedSessionResult> {
|
|
362
|
+
// ALI-1035 fail-fast: a hosted run cannot reach the model provider without a key
|
|
363
|
+
// (the firewall brokers the real key onto egress). Refuse BEFORE creating any
|
|
364
|
+
// session, run row, or sandbox — no orphaned rows, no paid box, instant error.
|
|
365
|
+
if (!opts.modelApiKey) {
|
|
366
|
+
throw new Error('hosted session needs a model API key: set ANTHROPIC_API_KEY (or pass --model-key)')
|
|
367
|
+
}
|
|
362
368
|
const rawFetch = opts.rawFetch ?? (globalThis.fetch as HostedFetch)
|
|
363
369
|
const log = opts.logLine ?? ((): void => {})
|
|
364
370
|
const model = opts.model ?? DEFAULT_HOSTED_MODEL
|
|
@@ -602,6 +608,32 @@ export async function startHostedSession(
|
|
|
602
608
|
let sandboxBudgetMs = durationMs
|
|
603
609
|
let tokenExpiresAtMs = now() + ttlMinutes * 60 * 1000
|
|
604
610
|
let stopped = false
|
|
611
|
+
|
|
612
|
+
// ALI-1037 host-side idle gate: only KEEP a sandbox alive while its run is
|
|
613
|
+
// making progress. Before each keepalive extension we cheaply check whether any
|
|
614
|
+
// new run event landed since the last check; a run with NO new events since the
|
|
615
|
+
// previous rotation cycle is not extended (the in-sandbox watchdog is the
|
|
616
|
+
// primary stall killer — this stops the HOST from paying to keep a dead box
|
|
617
|
+
// alive). "Can't tell" (non-2xx / transport error) never starves a healthy run.
|
|
618
|
+
let lastEventCursor = 0
|
|
619
|
+
const hasEventProgress = async (): Promise<boolean> => {
|
|
620
|
+
try {
|
|
621
|
+
const res = await opts.fetcher(
|
|
622
|
+
`/api/cli/workbench-runs/${encodeURIComponent(runId)}/events?after=${lastEventCursor}&limit=500`
|
|
623
|
+
)
|
|
624
|
+
if (!res.ok) return true
|
|
625
|
+
const body = (await res.json().catch(() => ({}))) as { events?: unknown }
|
|
626
|
+
const events = Array.isArray(body.events) ? (body.events as Record<string, unknown>[]) : []
|
|
627
|
+
if (events.length === 0) return false
|
|
628
|
+
lastEventCursor = events.reduce((max, event) => {
|
|
629
|
+
const seq = typeof event.sequence === 'number' ? event.sequence : 0
|
|
630
|
+
return seq > max ? seq : max
|
|
631
|
+
}, lastEventCursor)
|
|
632
|
+
return true
|
|
633
|
+
} catch {
|
|
634
|
+
return true
|
|
635
|
+
}
|
|
636
|
+
}
|
|
605
637
|
let cancelSleep: (() => void) | null = null
|
|
606
638
|
const stop = (): void => {
|
|
607
639
|
stopped = true
|
|
@@ -674,14 +706,21 @@ export async function startHostedSession(
|
|
|
674
706
|
log('rotated agent bearer (file overwritten)')
|
|
675
707
|
// Keepalive: extend the sandbox by one rotation interval each rotation
|
|
676
708
|
// while attached (SDK supports extendTimeout(ms)), bounded by the 24h cap
|
|
677
|
-
// so a long attached session outlives the initial --duration window
|
|
709
|
+
// so a long attached session outlives the initial --duration window — BUT
|
|
710
|
+
// only while the run is making progress (ALI-1037). A run with no new
|
|
711
|
+
// events since the last rotation is not extended: the box is left to wind
|
|
712
|
+
// down to its current budget instead of the host paying for a stalled run.
|
|
678
713
|
if (sandbox.extendTimeout && sandboxBudgetMs + rotationIntervalMs <= sandboxCapMs) {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
714
|
+
if (!(await hasEventProgress())) {
|
|
715
|
+
log('sandbox timeout NOT extended: no run events since the last rotation (agent stalled) — letting the box wind down')
|
|
716
|
+
} else {
|
|
717
|
+
try {
|
|
718
|
+
await sandbox.extendTimeout(rotationIntervalMs)
|
|
719
|
+
sandboxBudgetMs += rotationIntervalMs
|
|
720
|
+
log(`extended sandbox timeout (+${Math.round(rotationIntervalMs / 1000)}s, budget ${Math.round(sandboxBudgetMs / 60000)}m)`)
|
|
721
|
+
} catch (error) {
|
|
722
|
+
log(`sandbox extendTimeout failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
723
|
+
}
|
|
685
724
|
}
|
|
686
725
|
}
|
|
687
726
|
} catch (error) {
|
|
@@ -762,7 +801,7 @@ const HOSTED_USAGE =
|
|
|
762
801
|
'[--model <provider/model>] [--project <team/project>] [--provider vercel|local-sim] ' +
|
|
763
802
|
'[--image <orizu-hosted-runtime:tag> (pre-baked VCR image; default env ORIZU_HOSTED_IMAGE)] ' +
|
|
764
803
|
'[--snapshot <snapshot-id> (pre-baked Vercel snapshot, zero-Docker; default env ORIZU_HOSTED_SNAPSHOT; ' +
|
|
765
|
-
'mutually exclusive with --image)] [--tail]\n' +
|
|
804
|
+
'mutually exclusive with --image)] [--model-key <key> (else ANTHROPIC_API_KEY; required)] [--tail]\n' +
|
|
766
805
|
'Note (v0): if you disconnect, the agent token expires within its TTL (<=60m) and the run finishes ' +
|
|
767
806
|
'cleanly; reconnect with `orizu run tail --run <id>`. The sandbox timeout is independent (up to 24h).'
|
|
768
807
|
|
|
@@ -821,7 +860,9 @@ export async function hostedCommand(args: string[], io: HostedCommandIo): Promis
|
|
|
821
860
|
hostedImage: argVal(args, '--image') ?? process.env.ORIZU_HOSTED_IMAGE ?? undefined,
|
|
822
861
|
hostedSnapshot: argVal(args, '--snapshot') ?? process.env.ORIZU_HOSTED_SNAPSHOT ?? undefined,
|
|
823
862
|
tail,
|
|
824
|
-
|
|
863
|
+
// Model key: explicit --model-key wins, else ANTHROPIC_API_KEY. Falsy → the
|
|
864
|
+
// fail-fast in startHostedSession throws before any session/sandbox is made.
|
|
865
|
+
modelApiKey: argVal(args, '--model-key') ?? (process.env.ANTHROPIC_API_KEY || undefined),
|
|
825
866
|
logLine: line => io.print(line),
|
|
826
867
|
onTailEvent: event =>
|
|
827
868
|
io.print(io.json ? JSON.stringify(event) : `#${String(event.sequence ?? '?')} ${String(event.eventType ?? '')}`),
|
package/src/http.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getActiveBaseUrl,
|
|
3
|
+
getServerCredentials,
|
|
4
|
+
resolveEnvBearerToken,
|
|
5
|
+
updateServerCredentials,
|
|
6
|
+
} from './credentials.js'
|
|
2
7
|
import { getFlagBaseUrl, GlobalFlags, normalizeBaseUrl } from './global-flags.js'
|
|
3
8
|
import { LoginResponse, ServerCredentials, SessionServerCredentials } from './types.js'
|
|
4
9
|
|
|
@@ -122,6 +127,23 @@ async function refreshCredentials(baseUrl: string, credentials: ServerCredential
|
|
|
122
127
|
export async function authedFetch(path: string, init: RequestInit = {}) {
|
|
123
128
|
const baseUrl = resolveBaseUrl()
|
|
124
129
|
assertSecureTokenTransport(baseUrl)
|
|
130
|
+
|
|
131
|
+
// In-sandbox pre-auth (ALI-1044): a bearer supplied via ORIZU_TOKEN /
|
|
132
|
+
// ORIZU_TOKEN_FILE takes precedence over credentials.json. It is externally
|
|
133
|
+
// managed (the hosted loop rotates the token file), so it is read FRESH here on
|
|
134
|
+
// every request and NEVER refreshed by this client — on a 401 the loop rotates
|
|
135
|
+
// the file and the next request naturally picks up the new bearer.
|
|
136
|
+
const envBearer = resolveEnvBearerToken()
|
|
137
|
+
if (envBearer) {
|
|
138
|
+
return await fetch(`${baseUrl}${path}`, {
|
|
139
|
+
...init,
|
|
140
|
+
headers: {
|
|
141
|
+
...(init.headers || {}),
|
|
142
|
+
Authorization: `Bearer ${envBearer}`,
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
125
147
|
const credentials = getServerCredentials(baseUrl)
|
|
126
148
|
if (!credentials) {
|
|
127
149
|
throw new Error(`Not logged in for ${baseUrl}. Run \`orizu login --server ${baseUrl}\` (or \`--local\`) first.`)
|
package/src/index.ts
CHANGED
|
@@ -68,6 +68,11 @@ import { hostedCommand } from './hosted-session-cli.js'
|
|
|
68
68
|
import { workspaceSyncCommand } from './workspace-sync.js'
|
|
69
69
|
import { runGitCredential } from './git-credential.js'
|
|
70
70
|
import { type GithubLinkResult, runGithubLink, runInteractiveHostedSetup } from './github-setup.js'
|
|
71
|
+
import {
|
|
72
|
+
reportCommentsCommand,
|
|
73
|
+
throwDeprecatedPromptCommentsCommand,
|
|
74
|
+
type ReportCommentsCliContext,
|
|
75
|
+
} from './report-comments-cli.js'
|
|
71
76
|
|
|
72
77
|
function getErrorMessage(error: unknown): string {
|
|
73
78
|
return error instanceof Error ? error.message : String(error)
|
|
@@ -144,60 +149,18 @@ interface PromptSummary {
|
|
|
144
149
|
description?: string | null
|
|
145
150
|
}
|
|
146
151
|
|
|
147
|
-
interface
|
|
148
|
-
|
|
149
|
-
initials?: string
|
|
150
|
-
color?: string
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
interface PromptCommentAnchor {
|
|
154
|
-
text: string | null
|
|
155
|
-
startLine: number | null
|
|
156
|
-
endLine: number | null
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
interface PromptCommentReply {
|
|
160
|
-
id: string
|
|
161
|
-
body: string
|
|
162
|
-
author: PromptCommentAuthor
|
|
163
|
-
createdAt: string
|
|
164
|
-
updatedAt: string
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
interface PromptCommentThread {
|
|
168
|
-
id: string
|
|
169
|
-
status: 'open' | 'resolved'
|
|
170
|
-
body: string
|
|
171
|
-
anchor: PromptCommentAnchor | null
|
|
172
|
-
author: PromptCommentAuthor
|
|
173
|
-
createdAt: string
|
|
174
|
-
updatedAt: string
|
|
175
|
-
resolvedAt: string | null
|
|
176
|
-
resolvedByUserId: string | null
|
|
177
|
-
replyCount: number
|
|
178
|
-
replies: PromptCommentReply[]
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
interface PromptCommentsPayload {
|
|
182
|
-
prompt: {
|
|
183
|
-
id: string
|
|
184
|
-
name: string
|
|
185
|
-
role: string
|
|
186
|
-
description?: string | null
|
|
187
|
-
}
|
|
188
|
-
version: {
|
|
152
|
+
interface TaskReportPayload {
|
|
153
|
+
task: {
|
|
189
154
|
id: string
|
|
190
|
-
|
|
191
|
-
versionLabel?: string | null
|
|
155
|
+
title?: string | null
|
|
192
156
|
status?: string | null
|
|
157
|
+
report: {
|
|
158
|
+
markdown: string
|
|
159
|
+
sourceName: string | null
|
|
160
|
+
createdAt: string | null
|
|
161
|
+
updatedAt?: string | null
|
|
162
|
+
}
|
|
193
163
|
}
|
|
194
|
-
summary: {
|
|
195
|
-
threadCount: number
|
|
196
|
-
openThreadCount: number
|
|
197
|
-
resolvedThreadCount: number
|
|
198
|
-
replyCount: number
|
|
199
|
-
}
|
|
200
|
-
comments: PromptCommentThread[]
|
|
201
164
|
}
|
|
202
165
|
|
|
203
166
|
interface ScorerSummary {
|
|
@@ -1083,78 +1046,6 @@ function printTaskStatusSummary(data: TaskStatusPayload) {
|
|
|
1083
1046
|
}
|
|
1084
1047
|
}
|
|
1085
1048
|
|
|
1086
|
-
function compactTerminalText(value: string, maxLength = 160): string {
|
|
1087
|
-
const compact = sanitizeTerminalText(value).replace(/\s+/g, ' ').trim()
|
|
1088
|
-
if (compact.length <= maxLength) {
|
|
1089
|
-
return compact
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
return `${compact.slice(0, Math.max(0, maxLength - 1)).trim()}…`
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
function printIndentedBody(value: string, indent: string) {
|
|
1096
|
-
const lines = sanitizeTerminalText(value || '').split(/\r?\n/)
|
|
1097
|
-
for (const line of lines) {
|
|
1098
|
-
printLine(`${indent}${line}`)
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
function formatPromptCommentAnchor(anchor: PromptCommentAnchor | null): string | null {
|
|
1103
|
-
if (!anchor) {
|
|
1104
|
-
return null
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
const lineLabel = anchor.startLine && anchor.endLine
|
|
1108
|
-
? anchor.startLine === anchor.endLine
|
|
1109
|
-
? `line ${anchor.startLine}`
|
|
1110
|
-
: `lines ${anchor.startLine}-${anchor.endLine}`
|
|
1111
|
-
: null
|
|
1112
|
-
const selectedText = anchor.text ? `"${compactTerminalText(anchor.text, 180)}"` : null
|
|
1113
|
-
|
|
1114
|
-
if (lineLabel && selectedText) {
|
|
1115
|
-
return `${lineLabel}: ${selectedText}`
|
|
1116
|
-
}
|
|
1117
|
-
return lineLabel || selectedText
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
function printPromptComments(data: PromptCommentsPayload) {
|
|
1121
|
-
const versionLabel = data.version.versionLabel || (
|
|
1122
|
-
data.version.versionNumber ? `v${data.version.versionNumber}` : data.version.id
|
|
1123
|
-
)
|
|
1124
|
-
|
|
1125
|
-
printLine(`Prompt: ${sanitizeTerminalText(data.prompt.name)} (${sanitizeTerminalText(data.prompt.id)})`)
|
|
1126
|
-
printLine(`Version: ${sanitizeTerminalText(String(versionLabel))} (${sanitizeTerminalText(data.version.id)})`)
|
|
1127
|
-
printLine(
|
|
1128
|
-
`Comments: ${data.summary.threadCount} thread${data.summary.threadCount === 1 ? '' : 's'} ` +
|
|
1129
|
-
`(${data.summary.openThreadCount} open, ${data.summary.resolvedThreadCount} resolved, ${data.summary.replyCount} replies)`
|
|
1130
|
-
)
|
|
1131
|
-
|
|
1132
|
-
if (data.comments.length === 0) {
|
|
1133
|
-
printLine('\nNo comments found for this prompt version.')
|
|
1134
|
-
return
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
for (const [index, thread] of data.comments.entries()) {
|
|
1138
|
-
const authorName = thread.author?.name || 'Unknown'
|
|
1139
|
-
const anchor = formatPromptCommentAnchor(thread.anchor)
|
|
1140
|
-
printLine('')
|
|
1141
|
-
printLine(`${index + 1}. [${thread.status}] ${sanitizeTerminalText(authorName)} · ${sanitizeTerminalText(thread.createdAt)}`)
|
|
1142
|
-
if (anchor) {
|
|
1143
|
-
printLine(` Selection: ${anchor}`)
|
|
1144
|
-
}
|
|
1145
|
-
printIndentedBody(thread.body, ' ')
|
|
1146
|
-
|
|
1147
|
-
if (thread.replies.length > 0) {
|
|
1148
|
-
printLine(` Replies (${thread.replies.length})`)
|
|
1149
|
-
for (const reply of thread.replies) {
|
|
1150
|
-
const replyAuthor = reply.author?.name || 'Unknown'
|
|
1151
|
-
printLine(` - ${sanitizeTerminalText(replyAuthor)} · ${sanitizeTerminalText(reply.createdAt)}`)
|
|
1152
|
-
printIndentedBody(reply.body, ' ')
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
1049
|
const DEFAULT_AUTH_CALLBACK_PORT = 43123
|
|
1159
1050
|
|
|
1160
1051
|
function resolveAuthCallbackPort(): number {
|
|
@@ -1562,37 +1453,6 @@ async function listJudges() {
|
|
|
1562
1453
|
printPromptSummaries(data.judges, 'No judges found.')
|
|
1563
1454
|
}
|
|
1564
1455
|
|
|
1565
|
-
async function listPromptComments() {
|
|
1566
|
-
const promptRef = getPositionalArg(2)
|
|
1567
|
-
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
1568
|
-
const label = getArg('--label')
|
|
1569
|
-
const version = getArg('--version')
|
|
1570
|
-
|
|
1571
|
-
if (!promptRef) {
|
|
1572
|
-
throw new Error('Usage: orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <version-id>] [--json]')
|
|
1573
|
-
}
|
|
1574
|
-
if (label && version) {
|
|
1575
|
-
throw new Error('Use either --label or --version, not both')
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
const params = new URLSearchParams({ project })
|
|
1579
|
-
if (label) params.set('label', label)
|
|
1580
|
-
if (version) params.set('version', version)
|
|
1581
|
-
|
|
1582
|
-
const response = await authedFetch(`/api/cli/prompts/${encodeURIComponent(promptRef)}/comments?${params.toString()}`)
|
|
1583
|
-
if (!response.ok) {
|
|
1584
|
-
throw new Error(`Failed to fetch prompt comments: ${await response.text()}`)
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
const data = await parseJsonResponse<PromptCommentsPayload>(response, 'Prompt comments')
|
|
1588
|
-
if (hasJsonFlag()) {
|
|
1589
|
-
printJson(data as unknown as Record<string, unknown>)
|
|
1590
|
-
return
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
printPromptComments(data)
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
1456
|
async function listScorers() {
|
|
1597
1457
|
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
1598
1458
|
const response = await authedFetch(`/api/cli/scorers?project=${encodeURIComponent(project)}`)
|
|
@@ -2957,6 +2817,22 @@ function printJson(value: Record<string, unknown>) {
|
|
|
2957
2817
|
printLine(JSON.stringify(value))
|
|
2958
2818
|
}
|
|
2959
2819
|
|
|
2820
|
+
function createReportCommentsCliContext(): ReportCommentsCliContext {
|
|
2821
|
+
return {
|
|
2822
|
+
getArg,
|
|
2823
|
+
getPositionalArg,
|
|
2824
|
+
rejectDashPrefixedOptionValue,
|
|
2825
|
+
resolveProjectSlug,
|
|
2826
|
+
readSourceFile,
|
|
2827
|
+
authedFetch,
|
|
2828
|
+
parseJsonResponse,
|
|
2829
|
+
hasJsonFlag,
|
|
2830
|
+
printJson,
|
|
2831
|
+
printLine,
|
|
2832
|
+
sanitizeTerminalText,
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2960
2836
|
function printCapabilities() {
|
|
2961
2837
|
const capabilities = getCapabilities(getCliVersion())
|
|
2962
2838
|
if (hasJsonFlag()) {
|
|
@@ -4994,6 +4870,39 @@ async function setTaskReport() {
|
|
|
4994
4870
|
)
|
|
4995
4871
|
}
|
|
4996
4872
|
|
|
4873
|
+
async function getTaskReport() {
|
|
4874
|
+
const taskId = getArg('--task')
|
|
4875
|
+
rejectDashPrefixedOptionValue('--task', taskId)
|
|
4876
|
+
if (!taskId) {
|
|
4877
|
+
throw new Error('Usage: orizu tasks report get --task <taskId> [--json]')
|
|
4878
|
+
}
|
|
4879
|
+
|
|
4880
|
+
const response = await authedFetch(`/api/cli/tasks/${encodeURIComponent(taskId)}/report`)
|
|
4881
|
+
if (!response.ok) {
|
|
4882
|
+
throw new Error(`Failed to read task report: ${await response.text()}`)
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
const data = await parseJsonResponse<TaskReportPayload>(response, 'Task report get')
|
|
4886
|
+
if (hasJsonFlag()) {
|
|
4887
|
+
printJson(data as unknown as Record<string, unknown>)
|
|
4888
|
+
return
|
|
4889
|
+
}
|
|
4890
|
+
|
|
4891
|
+
const title = data.task.title ? `${data.task.title} ` : ''
|
|
4892
|
+
printLine(`Task report: ${sanitizeTerminalText(title)}(${sanitizeTerminalText(data.task.id)})`)
|
|
4893
|
+
if (data.task.status) {
|
|
4894
|
+
printLine(`Status: ${sanitizeTerminalText(data.task.status)}`)
|
|
4895
|
+
}
|
|
4896
|
+
if (data.task.report.sourceName) {
|
|
4897
|
+
printLine(`Source: ${sanitizeTerminalText(data.task.report.sourceName)}`)
|
|
4898
|
+
}
|
|
4899
|
+
if (data.task.report.createdAt) {
|
|
4900
|
+
printLine(`Created: ${sanitizeTerminalText(data.task.report.createdAt)}`)
|
|
4901
|
+
}
|
|
4902
|
+
printLine('')
|
|
4903
|
+
printLine(sanitizeTerminalText(data.task.report.markdown))
|
|
4904
|
+
}
|
|
4905
|
+
|
|
4997
4906
|
interface AppDetailPayload {
|
|
4998
4907
|
id: string
|
|
4999
4908
|
name: string
|
|
@@ -6339,6 +6248,11 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6339
6248
|
return
|
|
6340
6249
|
}
|
|
6341
6250
|
|
|
6251
|
+
if (command === 'comments') {
|
|
6252
|
+
await reportCommentsCommand(subcommand, createReportCommentsCliContext())
|
|
6253
|
+
return
|
|
6254
|
+
}
|
|
6255
|
+
|
|
6342
6256
|
if (command === 'teams' && subcommand === 'list') {
|
|
6343
6257
|
await listTeams()
|
|
6344
6258
|
return
|
|
@@ -6383,8 +6297,7 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6383
6297
|
}
|
|
6384
6298
|
|
|
6385
6299
|
if (command === 'prompts' && subcommand === 'comments') {
|
|
6386
|
-
|
|
6387
|
-
return
|
|
6300
|
+
throwDeprecatedPromptCommentsCommand()
|
|
6388
6301
|
}
|
|
6389
6302
|
|
|
6390
6303
|
if (command === 'prompts' && subcommand === 'pull') {
|
|
@@ -6594,6 +6507,11 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
6594
6507
|
return
|
|
6595
6508
|
}
|
|
6596
6509
|
|
|
6510
|
+
if (command === 'tasks' && subcommand === 'report' && cliArgs[2] === 'get') {
|
|
6511
|
+
await getTaskReport()
|
|
6512
|
+
return
|
|
6513
|
+
}
|
|
6514
|
+
|
|
6597
6515
|
if (command === 'tasks' && subcommand === 'pause') { await updateTaskStatus('paused'); return }
|
|
6598
6516
|
if (command === 'tasks' && subcommand === 'unpause') { await updateTaskStatus('active'); return }
|
|
6599
6517
|
if (command === 'tasks' && subcommand === 'complete') { await updateTaskStatus('completed'); return }
|
package/src/manifests-cli.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface ManifestsCommandIo {
|
|
|
16
16
|
resolveProjectSlug?: (arg: string | null) => Promise<string>
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const MANIFEST_ACTIONS = new Set(['approve', 'reject', 'apply'])
|
|
19
|
+
const MANIFEST_ACTIONS = new Set(['approve', 'reject', 'apply', 'revert'])
|
|
20
20
|
|
|
21
21
|
async function responseMessage(response: Response): Promise<string> {
|
|
22
22
|
try {
|
|
@@ -93,6 +93,28 @@ function num(value: unknown): number {
|
|
|
93
93
|
return typeof value === 'number' ? value : 0
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// Auto-applied manifests (ALI-1040) landed by SERVER POLICY, not a human approver
|
|
97
|
+
// (approved_by is NULL). Mark them so a reviewer can tell policy applies from
|
|
98
|
+
// human applies at a glance; the reason lives in the outcome for `show`.
|
|
99
|
+
function wasAutoApplied(manifest: Record<string, unknown>): boolean {
|
|
100
|
+
return asRecord(manifest.outcome).autoApplied === true
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** STATUS cell: `applied*` when the server auto-applied it, else the raw status. */
|
|
104
|
+
function statusDisplay(manifest: Record<string, unknown>): string {
|
|
105
|
+
const status = typeof manifest.status === 'string' ? manifest.status : '(unknown)'
|
|
106
|
+
return wasAutoApplied(manifest) ? `${status}*` : status
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** One-line auto-apply audit note for `show`, or null when not auto-applied. */
|
|
110
|
+
function autoApplyNote(manifest: Record<string, unknown>): string | null {
|
|
111
|
+
if (!wasAutoApplied(manifest)) return null
|
|
112
|
+
const reason = asRecord(manifest.outcome).autoApplyReason
|
|
113
|
+
return typeof reason === 'string' && reason.length > 0
|
|
114
|
+
? `auto-applied by policy: ${reason}`
|
|
115
|
+
: 'auto-applied by policy'
|
|
116
|
+
}
|
|
117
|
+
|
|
96
118
|
// -- list table (ALI-1038) ----------------------------------------------------
|
|
97
119
|
|
|
98
120
|
/** "3m" / "4h" / "2d" — enough resolution to pick a manifest out of a list. */
|
|
@@ -133,7 +155,7 @@ function manifestsTable(manifests: Record<string, unknown>[], now: number): stri
|
|
|
133
155
|
if (manifests.length === 0) return 'promotion manifests\n(none)'
|
|
134
156
|
const rows = [...manifests].sort(listOrder).map(manifest => ({
|
|
135
157
|
id: typeof manifest.id === 'string' ? manifest.id : '(unknown)',
|
|
136
|
-
status:
|
|
158
|
+
status: statusDisplay(manifest),
|
|
137
159
|
type: typeof manifest.actionType === 'string' ? manifest.actionType : '(unknown)',
|
|
138
160
|
changes: changesSummary(manifest),
|
|
139
161
|
author: typeof manifest.authorActorType === 'string' ? manifest.authorActorType : '-',
|
|
@@ -207,7 +229,9 @@ export async function manifestsCommand(args: string[], io: ManifestsCommandIo):
|
|
|
207
229
|
const response = await fetcherFrom(io)(`/api/cli/promotion-manifests/${encodeURIComponent(id)}`)
|
|
208
230
|
const data = await requireOk(response, 'Manifest show')
|
|
209
231
|
const manifest = (data.manifest ?? {}) as Record<string, unknown>
|
|
210
|
-
const
|
|
232
|
+
const base = manifest.actionType === 'repo_merge' ? repoMergeShow(manifest) : manifestLine(manifest)
|
|
233
|
+
const note = autoApplyNote(manifest)
|
|
234
|
+
const human = note ? `${base}\n ${note}` : base
|
|
211
235
|
emit(io, { manifest }, human)
|
|
212
236
|
return 0
|
|
213
237
|
}
|
|
@@ -244,6 +268,6 @@ export async function manifestsCommand(args: string[], io: ManifestsCommandIo):
|
|
|
244
268
|
return 0
|
|
245
269
|
}
|
|
246
270
|
|
|
247
|
-
io.print('Usage: orizu manifests <list|show|approve|reject|apply> ... [--json]')
|
|
271
|
+
io.print('Usage: orizu manifests <list|show|approve|reject|apply|revert> ... [--json]')
|
|
248
272
|
return 1
|
|
249
273
|
}
|