@stage5/lumine 0.2.68 → 0.2.70
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 +23 -6
- package/lib/admin-featured-history.js +185 -0
- package/lib/admin-featured.js +33 -0
- package/lib/admin-runtime-logs.js +45 -6
- package/lib/admin-workflows.js +52 -2
- package/lib/admin.js +269 -87
- package/lib/assets.js +11 -8
- package/lib/build-review.js +609 -8
- package/lib/commands.js +18 -9
- package/lib/constants.js +26 -1
- package/lib/sdk.js +114 -4
- package/lib/sponsor-duty.js +15 -2
- package/lib/thumbnail.js +2 -2
- package/package.json +1 -1
- package/sdk/BUILD_SDK_INDEX.md +72 -6
- package/sdk/LUMINE_ADMIN.md +344 -21
package/lib/admin.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from "./build-review.js";
|
|
24
24
|
import { runAdminRuntimeLogWorkflow } from "./admin-runtime-logs.js";
|
|
25
25
|
import { readApprovedFeaturedPlan, runFeaturedWorkflow } from "./admin-featured.js";
|
|
26
|
+
import { FEATURED_HISTORY_BATCH_SIZE, runBatchedFeaturedHistory } from "./admin-featured-history.js";
|
|
26
27
|
|
|
27
28
|
const MAX_EDITORIAL_FILE_BYTES = 256 * 1024;
|
|
28
29
|
const MAX_COMPOSED_TEXT_FILE_BYTES = 64 * 1024;
|
|
@@ -300,7 +301,10 @@ export async function adminCommand(options) {
|
|
|
300
301
|
"Use --resume with the scan checkpoint instead of combining --all with --cursor.",
|
|
301
302
|
);
|
|
302
303
|
}
|
|
303
|
-
|
|
304
|
+
const paginate = operation.name === "featured.history" &&
|
|
305
|
+
operation.pagination.filters.subjectIds.length > FEATURED_HISTORY_BATCH_SIZE
|
|
306
|
+
? runBatchedFeaturedHistory : runAutomaticPagination;
|
|
307
|
+
result = await paginate({
|
|
304
308
|
options,
|
|
305
309
|
operation,
|
|
306
310
|
runId,
|
|
@@ -329,6 +333,15 @@ export async function adminCommand(options) {
|
|
|
329
333
|
result = transformResult(await fetchOperation());
|
|
330
334
|
}
|
|
331
335
|
} catch (error) {
|
|
336
|
+
if (operation.name === "runtime.evidence" && error.status === 404) {
|
|
337
|
+
error.code = "CLI_ADMIN_RUNTIME_EVIDENCE_NOT_DEPLOYED";
|
|
338
|
+
error.message = "The runtime evidence route is not deployed on the requested host. Evidence is unknown; deploy the matching API and activate the collector in an authorized primary-generation release. No restart or host substitution was attempted.";
|
|
339
|
+
error.data = {
|
|
340
|
+
ok: false,
|
|
341
|
+
status: "unavailable",
|
|
342
|
+
error: { code: error.code, message: error.message, details: { httpStatus: 404 } },
|
|
343
|
+
};
|
|
344
|
+
}
|
|
332
345
|
if (operation.featuredWorkflow && error.featuredProgress) {
|
|
333
346
|
const serverError = error.data?.error;
|
|
334
347
|
error.data = {
|
|
@@ -388,6 +401,9 @@ export async function adminCommand(options) {
|
|
|
388
401
|
typeof operation.body.buildReviewUnderstanding === "string",
|
|
389
402
|
});
|
|
390
403
|
}
|
|
404
|
+
if (operation.name === "comment.edit") {
|
|
405
|
+
assertComposedCommentEditResult({ result, body: operation.body });
|
|
406
|
+
}
|
|
391
407
|
if (operation.name === "ai-email-policy.set") {
|
|
392
408
|
assertAiEmailPolicySetResult({ operation, result });
|
|
393
409
|
}
|
|
@@ -423,6 +439,7 @@ function transformAdminResult({
|
|
|
423
439
|
assertSubjectWindowResult({ operation, result: transformed });
|
|
424
440
|
}
|
|
425
441
|
if (operation.name === "builds.candidates") {
|
|
442
|
+
assertBuildWindowResult({ operation, result: transformed });
|
|
426
443
|
transformed = normalizeAdminBuildCandidatesResult({
|
|
427
444
|
result: transformed,
|
|
428
445
|
siteUrl: options.siteUrl,
|
|
@@ -558,12 +575,11 @@ export function writeAdminResultOutput({ filePath, result, operation }) {
|
|
|
558
575
|
}
|
|
559
576
|
|
|
560
577
|
export function normalizeAdminBuildCandidatesResult({ result, siteUrl }) {
|
|
561
|
-
const builds =
|
|
562
|
-
const nextCursor = String(result?.cursor || "").trim() || null;
|
|
578
|
+
const builds = result.data.builds;
|
|
563
579
|
return {
|
|
564
|
-
|
|
565
|
-
status: "success",
|
|
580
|
+
...result,
|
|
566
581
|
data: {
|
|
582
|
+
...result.data,
|
|
567
583
|
builds: builds.map((build) => {
|
|
568
584
|
const id = Number(build?.id || 0);
|
|
569
585
|
return {
|
|
@@ -580,15 +596,128 @@ export function normalizeAdminBuildCandidatesResult({ result, siteUrl }) {
|
|
|
580
596
|
},
|
|
581
597
|
};
|
|
582
598
|
}),
|
|
583
|
-
pagination: {
|
|
584
|
-
nextCursor,
|
|
585
|
-
hasMore: Boolean(nextCursor),
|
|
586
|
-
exhausted: !nextCursor,
|
|
587
|
-
},
|
|
588
599
|
},
|
|
589
600
|
};
|
|
590
601
|
}
|
|
591
602
|
|
|
603
|
+
export function assertBuildWindowResult({ operation, result }) {
|
|
604
|
+
const p = result?.data?.pagination;
|
|
605
|
+
const mode = operation?.pagination?.coverageMode;
|
|
606
|
+
if (
|
|
607
|
+
result?.ok !== true ||
|
|
608
|
+
!Array.isArray(result?.data?.builds) ||
|
|
609
|
+
!p ||
|
|
610
|
+
p.mode !== mode ||
|
|
611
|
+
!Number.isSafeInteger(p.snapshotMaxId) ||
|
|
612
|
+
p.snapshotMaxId < 0 ||
|
|
613
|
+
!Number.isSafeInteger(p.snapshotTimeStamp) ||
|
|
614
|
+
p.snapshotTimeStamp < 0 ||
|
|
615
|
+
(mode === "legacy"
|
|
616
|
+
? p.after !== null
|
|
617
|
+
: !Number.isSafeInteger(p.after) || p.after < 0) ||
|
|
618
|
+
(mode === "after" && p.after !== operation.pagination.after) ||
|
|
619
|
+
typeof p.exhausted !== "boolean" ||
|
|
620
|
+
p.hasMore !== !p.exhausted ||
|
|
621
|
+
(p.exhausted
|
|
622
|
+
? p.nextCursor !== null
|
|
623
|
+
: typeof p.nextCursor !== "string" || !p.nextCursor)
|
|
624
|
+
) {
|
|
625
|
+
const error = new Error(
|
|
626
|
+
"The API did not confirm the requested published-Build window and snapshot. Deploy the matching API; do not substitute an unbounded public browser scan.",
|
|
627
|
+
);
|
|
628
|
+
error.code = "LUMINE_ADMIN_BUILD_WINDOW_UNSUPPORTED";
|
|
629
|
+
throw error;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
export function assertComposedCommentEditResult({ result, body }) {
|
|
634
|
+
const edit = result?.data?.edit;
|
|
635
|
+
if (
|
|
636
|
+
result?.ok === true &&
|
|
637
|
+
result?.data?.comment?.content === body.content &&
|
|
638
|
+
(!body.buildReviewUnderstanding ||
|
|
639
|
+
(edit?.buildReviewContextStored === true &&
|
|
640
|
+
edit?.reviewedBuildVersionId === body.reviewedBuildVersionId &&
|
|
641
|
+
Number.isSafeInteger(edit?.managementDraftId) &&
|
|
642
|
+
edit.managementDraftId > 0))
|
|
643
|
+
)
|
|
644
|
+
return;
|
|
645
|
+
const error = new Error(
|
|
646
|
+
"The API did not confirm the exact comment edit and its required Build review context. Inspect the canonical result before retrying; deploy the matching API if unsupported.",
|
|
647
|
+
);
|
|
648
|
+
error.code = "LUMINE_ADMIN_COMMENT_EDIT_UNCONFIRMED";
|
|
649
|
+
error.data = {
|
|
650
|
+
ok: false,
|
|
651
|
+
status: "validation_error",
|
|
652
|
+
error: {
|
|
653
|
+
code: error.code,
|
|
654
|
+
message: error.message,
|
|
655
|
+
details: { canonicalResult: result },
|
|
656
|
+
},
|
|
657
|
+
};
|
|
658
|
+
throw error;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function readAdminBuildReviewEvidence(options, parsedTarget) {
|
|
662
|
+
const version = options.adminReviewedBuildVersion
|
|
663
|
+
? parseRequiredInteger(
|
|
664
|
+
options.adminReviewedBuildVersion,
|
|
665
|
+
"--reviewed-version",
|
|
666
|
+
1,
|
|
667
|
+
)
|
|
668
|
+
: undefined;
|
|
669
|
+
const method = options.adminBuildReviewMethod
|
|
670
|
+
? parseAdminBuildReviewMethod(options.adminBuildReviewMethod)
|
|
671
|
+
: undefined;
|
|
672
|
+
const receipt = options.adminReviewReceipt
|
|
673
|
+
? parseBuildReviewReceipt(options.adminReviewReceipt)
|
|
674
|
+
: null;
|
|
675
|
+
const understanding = options.adminReviewContext
|
|
676
|
+
? readBuildReviewContextFile(options.adminReviewContext)
|
|
677
|
+
: undefined;
|
|
678
|
+
if (receipt && (version || method)) {
|
|
679
|
+
throw cliValidationError(
|
|
680
|
+
"Pass either --review-receipt or manual --reviewed-version/--reviewed-via evidence, not both.",
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
const reviewedBuildVersionId = receipt
|
|
684
|
+
? Number(receipt.publishedArtifactVersionId)
|
|
685
|
+
: version;
|
|
686
|
+
const buildReviewMethod = receipt ? "runtime" : method;
|
|
687
|
+
if (understanding && (!reviewedBuildVersionId || !buildReviewMethod)) {
|
|
688
|
+
throw cliValidationError(
|
|
689
|
+
"--review-context requires confirmed Build review evidence.",
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if ((reviewedBuildVersionId || buildReviewMethod) && !understanding) {
|
|
693
|
+
throw cliValidationError(
|
|
694
|
+
"Build review evidence requires --review-context <context.json>.",
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
if (
|
|
698
|
+
receipt &&
|
|
699
|
+
parsedTarget.type === "build" &&
|
|
700
|
+
Number(receipt.buildId) !== parsedTarget.id
|
|
701
|
+
) {
|
|
702
|
+
throw cliValidationError(
|
|
703
|
+
"The review receipt belongs to a different Build.",
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
if (
|
|
707
|
+
!["build", "comment"].includes(parsedTarget.type) &&
|
|
708
|
+
(reviewedBuildVersionId || buildReviewMethod)
|
|
709
|
+
) {
|
|
710
|
+
throw cliValidationError(
|
|
711
|
+
"Build review evidence applies only to build:<id> or a comment:<id> inside a Build.",
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
...(reviewedBuildVersionId ? { reviewedBuildVersionId } : {}),
|
|
716
|
+
...(buildReviewMethod ? { buildReviewMethod } : {}),
|
|
717
|
+
...(understanding ? { buildReviewUnderstanding: understanding } : {}),
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
592
721
|
export function assertComposedCommentDraftResult({
|
|
593
722
|
result,
|
|
594
723
|
expectedContent,
|
|
@@ -873,6 +1002,7 @@ function adminOperationRequiresRun(operation) {
|
|
|
873
1002
|
"identity.use",
|
|
874
1003
|
"identity.inspect",
|
|
875
1004
|
"economy.trace",
|
|
1005
|
+
"bot.context",
|
|
876
1006
|
"rescue.wordle.audit",
|
|
877
1007
|
"daily-run.start",
|
|
878
1008
|
"daily-run.status",
|
|
@@ -1445,11 +1575,13 @@ export function parseAdminOperation(options) {
|
|
|
1445
1575
|
}
|
|
1446
1576
|
|
|
1447
1577
|
if (namespace === "builds" && action === "candidates") {
|
|
1578
|
+
const window = parseBuildWindow(options);
|
|
1448
1579
|
return readOperation(
|
|
1449
1580
|
"builds.candidates",
|
|
1450
|
-
withQuery("/
|
|
1451
|
-
|
|
1452
|
-
|
|
1581
|
+
withQuery("/cli/admin/builds/candidates", {
|
|
1582
|
+
sinceRun: window.mode === "since-run" ? "true" : "",
|
|
1583
|
+
after: window.mode === "after" ? window.after : "",
|
|
1584
|
+
includeLegacy: window.mode === "legacy" ? "true" : "",
|
|
1453
1585
|
cursor: options.adminCursor,
|
|
1454
1586
|
limit: options.limit,
|
|
1455
1587
|
}),
|
|
@@ -1457,9 +1589,12 @@ export function parseAdminOperation(options) {
|
|
|
1457
1589
|
pagination: {
|
|
1458
1590
|
collectionKey: "builds",
|
|
1459
1591
|
coverageQueue: "builds",
|
|
1460
|
-
coverageMode:
|
|
1461
|
-
after:
|
|
1462
|
-
|
|
1592
|
+
coverageMode: window.mode,
|
|
1593
|
+
after:
|
|
1594
|
+
window.mode === "after"
|
|
1595
|
+
? parseAfterForCoverage(window.after)
|
|
1596
|
+
: null,
|
|
1597
|
+
filters: { sort: "published-release", scope: "all" },
|
|
1463
1598
|
},
|
|
1464
1599
|
},
|
|
1465
1600
|
);
|
|
@@ -1478,6 +1613,9 @@ export function parseAdminOperation(options) {
|
|
|
1478
1613
|
body: undefined,
|
|
1479
1614
|
mutates: false,
|
|
1480
1615
|
buildId: parsed.id,
|
|
1616
|
+
// Public artifact inspection also supports a one-comment correction;
|
|
1617
|
+
// it never needs a full daily-management authorization envelope.
|
|
1618
|
+
requiresRun: false,
|
|
1481
1619
|
};
|
|
1482
1620
|
}
|
|
1483
1621
|
|
|
@@ -1579,6 +1717,12 @@ export function parseAdminOperation(options) {
|
|
|
1579
1717
|
options.adminIds,
|
|
1580
1718
|
"--subject-ids",
|
|
1581
1719
|
);
|
|
1720
|
+
if (subjectIds.length > 20_000) {
|
|
1721
|
+
throw cliValidationError("Featured history accepts at most 20000 subject IDs per CLI scan.");
|
|
1722
|
+
}
|
|
1723
|
+
if (subjectIds.length > FEATURED_HISTORY_BATCH_SIZE && !options.adminAll) {
|
|
1724
|
+
throw cliValidationError("Pass --all to automatically batch history reads larger than 100 subjects.");
|
|
1725
|
+
}
|
|
1582
1726
|
return readOperation(
|
|
1583
1727
|
"featured.history",
|
|
1584
1728
|
withQuery("/cli/admin/subjects/featured/history", {
|
|
@@ -1589,6 +1733,7 @@ export function parseAdminOperation(options) {
|
|
|
1589
1733
|
{
|
|
1590
1734
|
pagination: {
|
|
1591
1735
|
collectionKey: "events",
|
|
1736
|
+
summaryKeys: ["coverage", "subjects"],
|
|
1592
1737
|
filters: { subjectIds },
|
|
1593
1738
|
},
|
|
1594
1739
|
},
|
|
@@ -1860,10 +2005,23 @@ export function parseAdminOperation(options) {
|
|
|
1860
2005
|
);
|
|
1861
2006
|
}
|
|
1862
2007
|
|
|
2008
|
+
if (namespace === "runtime") {
|
|
2009
|
+
if (action !== "evidence" || !["primary", "target"].includes(target) || extra) {
|
|
2010
|
+
throw cliValidationError("Usage: lumine admin runtime evidence primary|target [--days 1..7].");
|
|
2011
|
+
}
|
|
2012
|
+
return readOperation(
|
|
2013
|
+
"runtime.evidence",
|
|
2014
|
+
withQuery(`/cli/admin/runtime-logs/hosts/${target}/evidence`, {
|
|
2015
|
+
days: parseRequiredInteger(options.adminDays || "7", "--days", 1, 7),
|
|
2016
|
+
}),
|
|
2017
|
+
{ requiresRun: false },
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
|
|
1863
2021
|
if (namespace === "runtime-logs") {
|
|
1864
2022
|
if (
|
|
1865
2023
|
(action === "start" || action === "status" || action === "read") &&
|
|
1866
|
-
!target &&
|
|
2024
|
+
(!target || (action === "start" && ["primary", "target"].includes(target))) &&
|
|
1867
2025
|
!extra
|
|
1868
2026
|
) {
|
|
1869
2027
|
if (
|
|
@@ -1883,6 +2041,7 @@ export function parseAdminOperation(options) {
|
|
|
1883
2041
|
mutates: action !== "status",
|
|
1884
2042
|
requiresRun: false,
|
|
1885
2043
|
runtimeLogAction: action === "read" ? "capture" : action,
|
|
2044
|
+
...(action === "start" && target ? { runtimeLogHost: target } : {}),
|
|
1886
2045
|
};
|
|
1887
2046
|
}
|
|
1888
2047
|
if ((action === "resume" || action === "abandon") && !target && !extra) {
|
|
@@ -1978,6 +2137,36 @@ export function parseAdminOperation(options) {
|
|
|
1978
2137
|
});
|
|
1979
2138
|
}
|
|
1980
2139
|
|
|
2140
|
+
if (namespace === "bot-output" && action === "context") {
|
|
2141
|
+
const messageId = parseRequiredInteger(target, "bot message ID", 1);
|
|
2142
|
+
const reason = String(options.adminReason || "").trim();
|
|
2143
|
+
if (!reason || reason.length > MAX_IDENTITY_INSPECTION_REASON_LENGTH) {
|
|
2144
|
+
throw cliValidationError(
|
|
2145
|
+
"Explain the private bot investigation with --reason (1–500 characters).",
|
|
2146
|
+
);
|
|
2147
|
+
}
|
|
2148
|
+
if (options.adminAll || options.adminDays) {
|
|
2149
|
+
throw cliValidationError(
|
|
2150
|
+
"Bot context is a narrow message-ID investigation, not an --all or --days scan.",
|
|
2151
|
+
);
|
|
2152
|
+
}
|
|
2153
|
+
return writeOperation(
|
|
2154
|
+
"bot.context",
|
|
2155
|
+
"POST",
|
|
2156
|
+
`/cli/admin/bot-output/${messageId}/context`,
|
|
2157
|
+
{
|
|
2158
|
+
reason,
|
|
2159
|
+
cursor: options.adminCursor || undefined,
|
|
2160
|
+
limit: parseRequiredInteger(
|
|
2161
|
+
options.adminContextLimit ?? 20,
|
|
2162
|
+
"--limit",
|
|
2163
|
+
1,
|
|
2164
|
+
40,
|
|
2165
|
+
),
|
|
2166
|
+
},
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
|
|
1981
2170
|
if (namespace === "bot-output" && !action) {
|
|
1982
2171
|
if (options.adminDays && options.adminCursor) {
|
|
1983
2172
|
throw cliValidationError(
|
|
@@ -2029,58 +2218,10 @@ export function parseAdminOperation(options) {
|
|
|
2029
2218
|
"comment reply targets a comment: lumine admin comment reply comment:<id>.",
|
|
2030
2219
|
);
|
|
2031
2220
|
}
|
|
2032
|
-
const
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
1,
|
|
2037
|
-
)
|
|
2038
|
-
: undefined;
|
|
2039
|
-
const buildReviewMethod = options.adminBuildReviewMethod
|
|
2040
|
-
? parseAdminBuildReviewMethod(options.adminBuildReviewMethod)
|
|
2041
|
-
: undefined;
|
|
2042
|
-
const reviewReceipt = options.adminReviewReceipt
|
|
2043
|
-
? parseBuildReviewReceipt(options.adminReviewReceipt)
|
|
2044
|
-
: null;
|
|
2045
|
-
const buildReviewUnderstanding = options.adminReviewContext
|
|
2046
|
-
? readBuildReviewContextFile(options.adminReviewContext)
|
|
2047
|
-
: undefined;
|
|
2048
|
-
if (reviewReceipt && (reviewedBuildVersionId || buildReviewMethod)) {
|
|
2049
|
-
throw cliValidationError(
|
|
2050
|
-
"Pass either --review-receipt or manual --reviewed-version/--reviewed-via evidence, not both.",
|
|
2051
|
-
);
|
|
2052
|
-
}
|
|
2053
|
-
const confirmedBuildVersionId = reviewReceipt
|
|
2054
|
-
? Number(reviewReceipt.publishedArtifactVersionId)
|
|
2055
|
-
: reviewedBuildVersionId;
|
|
2056
|
-
const confirmedBuildReviewMethod = reviewReceipt
|
|
2057
|
-
? "runtime"
|
|
2058
|
-
: buildReviewMethod;
|
|
2059
|
-
if (
|
|
2060
|
-
buildReviewUnderstanding &&
|
|
2061
|
-
(!confirmedBuildVersionId || !confirmedBuildReviewMethod)
|
|
2062
|
-
) {
|
|
2063
|
-
throw cliValidationError(
|
|
2064
|
-
"--review-context requires confirmed Build review evidence.",
|
|
2065
|
-
);
|
|
2066
|
-
}
|
|
2067
|
-
if (
|
|
2068
|
-
(confirmedBuildVersionId || confirmedBuildReviewMethod) &&
|
|
2069
|
-
!buildReviewUnderstanding
|
|
2070
|
-
) {
|
|
2071
|
-
throw cliValidationError(
|
|
2072
|
-
"Build review evidence requires --review-context <context.json>.",
|
|
2073
|
-
);
|
|
2074
|
-
}
|
|
2075
|
-
if (
|
|
2076
|
-
reviewReceipt &&
|
|
2077
|
-
parsedTarget.type === "build" &&
|
|
2078
|
-
Number(reviewReceipt.buildId) !== parsedTarget.id
|
|
2079
|
-
) {
|
|
2080
|
-
throw cliValidationError(
|
|
2081
|
-
"The review receipt belongs to a different Build.",
|
|
2082
|
-
);
|
|
2083
|
-
}
|
|
2221
|
+
const reviewEvidence = readAdminBuildReviewEvidence(
|
|
2222
|
+
options,
|
|
2223
|
+
parsedTarget,
|
|
2224
|
+
);
|
|
2084
2225
|
if (parsedTarget.type === "build") {
|
|
2085
2226
|
if (!options.adminFile) {
|
|
2086
2227
|
throw cliValidationError(
|
|
@@ -2088,21 +2229,14 @@ export function parseAdminOperation(options) {
|
|
|
2088
2229
|
);
|
|
2089
2230
|
}
|
|
2090
2231
|
if (
|
|
2091
|
-
!
|
|
2092
|
-
!
|
|
2093
|
-
!buildReviewUnderstanding
|
|
2232
|
+
!reviewEvidence.reviewedBuildVersionId ||
|
|
2233
|
+
!reviewEvidence.buildReviewMethod ||
|
|
2234
|
+
!reviewEvidence.buildReviewUnderstanding
|
|
2094
2235
|
) {
|
|
2095
2236
|
throw cliValidationError(
|
|
2096
2237
|
"After reviewing the project, pass review evidence and --review-context <context.json>.",
|
|
2097
2238
|
);
|
|
2098
2239
|
}
|
|
2099
|
-
} else if (
|
|
2100
|
-
parsedTarget.type !== "comment" &&
|
|
2101
|
-
(confirmedBuildVersionId || confirmedBuildReviewMethod)
|
|
2102
|
-
) {
|
|
2103
|
-
throw cliValidationError(
|
|
2104
|
-
"Build review evidence applies only to build:<id> or a comment:<id> inside a Build.",
|
|
2105
|
-
);
|
|
2106
2240
|
}
|
|
2107
2241
|
return writeOperation(
|
|
2108
2242
|
"comment.draft",
|
|
@@ -2117,13 +2251,7 @@ export function parseAdminOperation(options) {
|
|
|
2117
2251
|
...(options.adminFile
|
|
2118
2252
|
? { content: readComposedTextFile(options.adminFile) }
|
|
2119
2253
|
: {}),
|
|
2120
|
-
...
|
|
2121
|
-
? { reviewedBuildVersionId: confirmedBuildVersionId }
|
|
2122
|
-
: {}),
|
|
2123
|
-
...(confirmedBuildReviewMethod
|
|
2124
|
-
? { buildReviewMethod: confirmedBuildReviewMethod }
|
|
2125
|
-
: {}),
|
|
2126
|
-
...(buildReviewUnderstanding ? { buildReviewUnderstanding } : {}),
|
|
2254
|
+
...reviewEvidence,
|
|
2127
2255
|
},
|
|
2128
2256
|
);
|
|
2129
2257
|
}
|
|
@@ -2148,7 +2276,13 @@ export function parseAdminOperation(options) {
|
|
|
2148
2276
|
"comment.edit",
|
|
2149
2277
|
"PUT",
|
|
2150
2278
|
`/cli/admin/comments/${commentId}`,
|
|
2151
|
-
{
|
|
2279
|
+
{
|
|
2280
|
+
content: readComposedTextFile(options.adminFile),
|
|
2281
|
+
...readAdminBuildReviewEvidence(options, {
|
|
2282
|
+
type: "comment",
|
|
2283
|
+
id: commentId,
|
|
2284
|
+
}),
|
|
2285
|
+
},
|
|
2152
2286
|
{
|
|
2153
2287
|
correctionEligible: true,
|
|
2154
2288
|
correctionCommentId: commentId,
|
|
@@ -2394,6 +2528,22 @@ export function parseSubjectWindow(options) {
|
|
|
2394
2528
|
return { mode, after: mode === "after" ? options.adminAfter : "" };
|
|
2395
2529
|
}
|
|
2396
2530
|
|
|
2531
|
+
export function parseBuildWindow(options) {
|
|
2532
|
+
const selected = [
|
|
2533
|
+
options.adminSinceRun ? "since-run" : "",
|
|
2534
|
+
options.adminAfter ? "after" : "",
|
|
2535
|
+
options.adminIncludeLegacy ? "legacy" : "",
|
|
2536
|
+
].filter(Boolean);
|
|
2537
|
+
if (selected.length > 1) {
|
|
2538
|
+
throw cliValidationError(
|
|
2539
|
+
"Choose one Build window: --since-run, --after, or --include-legacy.",
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
2542
|
+
const mode = selected[0] || "since-run";
|
|
2543
|
+
if (mode === "after") parseAfterForCoverage(options.adminAfter);
|
|
2544
|
+
return { mode, after: mode === "after" ? options.adminAfter : "" };
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2397
2547
|
function parseUtcDayKey(value) {
|
|
2398
2548
|
const dayKey = String(value || "").trim();
|
|
2399
2549
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dayKey);
|
|
@@ -3247,6 +3397,16 @@ async function printSpooledAdminResult({ operation, result, storage }) {
|
|
|
3247
3397
|
|
|
3248
3398
|
function printAdminResult({ operation, result }) {
|
|
3249
3399
|
const data = result?.data || {};
|
|
3400
|
+
if (operation.name === "runtime.evidence") {
|
|
3401
|
+
const evidence = data.evidence || {};
|
|
3402
|
+
console.log(`Runtime evidence (${data.host?.requested || "unknown"}): ${evidence.status || "unknown"}.`);
|
|
3403
|
+
console.log(`Samples: ${evidence.coverage?.samples ?? "unknown"}; latest: ${evidence.coverage?.lastAtMs ?? "unknown"}; age ms: ${evidence.coverage?.ageMs ?? "unknown"}.`);
|
|
3404
|
+
for (const recycle of evidence.recycles || []) {
|
|
3405
|
+
console.log(` ${recycle.id}: ${recycle.outcome}; under load: ${recycle.observedUnderLoad ?? "unknown"}.`);
|
|
3406
|
+
}
|
|
3407
|
+
console.log("Missing evidence is unknown, not healthy. A recovered topology does not verify interrupted user work. Use --json for full evidence.");
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3250
3410
|
if (operation.name === "featured.plan") {
|
|
3251
3411
|
for (const pair of data.plan?.replacements || []) {
|
|
3252
3412
|
console.log(`${pair.remove.id} ${pair.remove.title} -> ${pair.add.id} ${pair.add.title}`);
|
|
@@ -3360,6 +3520,14 @@ function printAdminResult({ operation, result }) {
|
|
|
3360
3520
|
`Confirmed Build #${data.review.buildId} runtime at published artifact #${data.review.publishedArtifactVersionId}.`,
|
|
3361
3521
|
);
|
|
3362
3522
|
console.log(`Screenshot: ${data.screenshotPath}`);
|
|
3523
|
+
if (data.review.interaction) {
|
|
3524
|
+
console.log(
|
|
3525
|
+
`Interaction script: ${data.review.interaction.stepsCompleted}/${data.review.interaction.stepsPlanned} step(s) ${data.review.interaction.status}.`,
|
|
3526
|
+
);
|
|
3527
|
+
}
|
|
3528
|
+
for (const shot of data.review.screenshots || []) {
|
|
3529
|
+
console.log(` Screenshot [${shot.label}]: ${shot.path}`);
|
|
3530
|
+
}
|
|
3363
3531
|
console.log(`Review receipt: ${data.receiptPath}`);
|
|
3364
3532
|
return;
|
|
3365
3533
|
}
|
|
@@ -3508,6 +3676,20 @@ function printAdminResult({ operation, result }) {
|
|
|
3508
3676
|
);
|
|
3509
3677
|
return;
|
|
3510
3678
|
}
|
|
3679
|
+
if (data.escalation?.summary) {
|
|
3680
|
+
const target =
|
|
3681
|
+
data.escalation.url ||
|
|
3682
|
+
`${data.escalation.targetType || "target"}:${data.escalation.targetId || "?"}`;
|
|
3683
|
+
console.log(
|
|
3684
|
+
`Escalation${data.escalation.auditId ? ` #${data.escalation.auditId}` : ""} recorded: ${String(data.escalation.severity || "attention").toUpperCase()} ${target} — ${data.escalation.summary}`,
|
|
3685
|
+
);
|
|
3686
|
+
if (data.escalation.auditId) {
|
|
3687
|
+
console.log(
|
|
3688
|
+
`Set its disposition later with: lumine admin escalation set ${data.escalation.auditId} --status <status> --note <decision>`,
|
|
3689
|
+
);
|
|
3690
|
+
}
|
|
3691
|
+
return;
|
|
3692
|
+
}
|
|
3511
3693
|
if (Array.isArray(data.todos)) {
|
|
3512
3694
|
printTodoItems(data.todos, "Private carry-over work");
|
|
3513
3695
|
if (data.truncated) {
|
package/lib/assets.js
CHANGED
|
@@ -56,7 +56,7 @@ export async function assetsCommand(options) {
|
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
throw new Error(
|
|
59
|
-
'Usage: lumine assets [list] | lumine assets upload <file...> | lumine assets generate "<prompt>" --model <gpt-image-2|nano-banana> | lumine assets delete <assetId> | lumine assets prune [--yes]',
|
|
59
|
+
'Usage: lumine assets [list] | lumine assets upload <file...> | lumine assets generate "<prompt>" --model <gpt-image-2.5-flare|gpt-image-2.5-sunburst|gpt-image-2|nano-banana> | lumine assets delete <assetId> | lumine assets prune [--yes]',
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -65,18 +65,21 @@ export function resolveGenerateModel(options) {
|
|
|
65
65
|
const model = GENERATE_MODEL_ALIASES[rawModel];
|
|
66
66
|
if (!model) {
|
|
67
67
|
throw new Error(
|
|
68
|
-
"--model is required: gpt-image-2 (
|
|
68
|
+
"--model is required: gpt-image-2.5-flare (fast generation), gpt-image-2.5-sunburst (precise editing), gpt-image-2, or nano-banana. No default model is applied.",
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
const rawQuality = String(options.quality || "").trim();
|
|
72
72
|
if (rawQuality && !GENERATE_QUALITIES.has(rawQuality)) {
|
|
73
|
-
throw new Error("--quality must be low, medium, or
|
|
73
|
+
throw new Error("--quality must be low, medium, high, xhigh, or max.");
|
|
74
74
|
}
|
|
75
|
-
if (rawQuality && model
|
|
75
|
+
if (rawQuality && !model.startsWith("gpt-image-")) {
|
|
76
76
|
throw new Error(
|
|
77
|
-
"--quality only applies to
|
|
77
|
+
"--quality only applies to GPT Image models; nano-banana has a single quality tier.",
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
|
+
if (model === "gpt-image-2" && ["xhigh", "max"].includes(rawQuality)) {
|
|
81
|
+
throw new Error("xhigh and max require a GPT Image 2.5 model.");
|
|
82
|
+
}
|
|
80
83
|
return { model, quality: rawQuality || null };
|
|
81
84
|
}
|
|
82
85
|
|
|
@@ -118,7 +121,7 @@ export async function assetsGenerate(options) {
|
|
|
118
121
|
const prompt = String(options.positional[1] || "").trim();
|
|
119
122
|
if (!prompt) {
|
|
120
123
|
throw new Error(
|
|
121
|
-
'Usage: lumine assets generate "<prompt>" --model <gpt-image-2|nano-banana> [--quality low|medium|high] [--name <fileName>] [--yes]',
|
|
124
|
+
'Usage: lumine assets generate "<prompt>" --model <gpt-image-2.5-flare|gpt-image-2.5-sunburst|gpt-image-2|nano-banana> [--quality low|medium|high|xhigh|max] [--name <fileName>] [--yes]',
|
|
122
125
|
);
|
|
123
126
|
}
|
|
124
127
|
const { model, quality } = resolveGenerateModel(options);
|
|
@@ -161,8 +164,8 @@ export async function assetsGenerate(options) {
|
|
|
161
164
|
(option) => option.model === model,
|
|
162
165
|
);
|
|
163
166
|
const costLine = selectedOption
|
|
164
|
-
? ` Estimated
|
|
165
|
-
: " Estimated
|
|
167
|
+
? ` Estimated image output: ${formatBatteryPercent(selectedOption.energyUnits, estimate?.fullBatteryUnits)} of a full AI battery (~$${Number(selectedOption.estimatedUsd || 0).toFixed(2)}). Prompt input uses additional energy.`
|
|
168
|
+
: " Estimated image output: unavailable";
|
|
166
169
|
const remainingLine = estimate
|
|
167
170
|
? ` Battery remaining now: ${formatBatteryPercent(estimate.energyRemaining, estimate.fullBatteryUnits)}`
|
|
168
171
|
: "";
|