@stage5/lumine 0.2.52 → 0.2.54

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 CHANGED
@@ -139,6 +139,10 @@ methods. The JSON args are sent as the request body (shapes follow
139
139
  ```bash
140
140
  lumine sdk call aiStories.chapters '{"limit": 5}'
141
141
  lumine sdk call aiStories.list '{"difficulty": 1}' --repeat 5 --build 1374
142
+ lumine sdk call live.list '{}'
143
+ lumine sdk call live.get '{"sessionId": "..."}'
144
+ lumine sdk call live.listReplays '{"limit": 20}'
145
+ lumine sdk call live.getReplay '{"replayId": "..."}'
142
146
  ```
143
147
 
144
148
  It targets the build in the current workspace, or pass `--build <id>`. Add
@@ -146,6 +150,13 @@ It targets the build in the current workspace, or pass `--build <id>`. Add
146
150
  which can differ from a method's `Twinkle.*` SDK return shape — check
147
151
  `TWINKLE_BUILD_SDK.md` for SDK return shapes. Methods that change data require
148
152
  `--allow-write`.
153
+ Stopping a hosted stream is available as
154
+ `lumine sdk call live.stop '{"sessionId":"..."}' --allow-write`; the command
155
+ prints canonical server state and refuses to mint `live:write` without the
156
+ explicit write flag.
157
+ Replay listing and status are available without exposing private playback
158
+ grants. A creator or app owner can remove one with
159
+ `lumine sdk call live.deleteReplay '{"replayId":"..."}' --allow-write`.
149
160
 
150
161
  ## Assets and AI image generation
151
162
 
@@ -218,6 +229,46 @@ claude "Read CLAUDE.md, then make the requested change."
218
229
  The login command uses a browser approval code and stores a scoped token and the
219
230
  selected project at `~/.twinkle/lumine-cli-auth.json`.
220
231
 
232
+ ## Sponsoring Zero or Ciel Build Workshop duty
233
+
234
+ Sponsorship is an approved contribution role, not an open switch and not a
235
+ website-management role. Applications are submitted only through Lumine CLI;
236
+ the Twinkle website provides instructions, but no application form.
237
+
238
+ ```bash
239
+ lumine sponsor agreement
240
+ lumine sponsor apply --providers codex --motivation "Why I want to help" \
241
+ --availability "My usual duty window" --accept-agreement
242
+ lumine sponsor status
243
+ ```
244
+
245
+ After Mikey approves an application, configure conservative limits based on
246
+ the subscription you are contributing, then start one foreground duty:
247
+
248
+ ```bash
249
+ lumine sponsor capacity --concurrency 1 --helpers 0 \
250
+ --daily-limit 3 --weekly-limit 10
251
+ lumine sponsor duty start zero --provider codex \
252
+ --model gpt-5.6-sol --effort max --service-tier priority
253
+ ```
254
+
255
+ While that foreground process has a fresh server lease, Zero or Ciel exposes a
256
+ Build Workshop in chat and users may see the named sponsor and canonical queue.
257
+ With no live duty, the website stays in its ordinary chat state and shows none
258
+ of the Workshop UI. Press Ctrl-C to stop accepting work and drain already
259
+ claimed jobs before duty ends; `pause`, `resume`, and `stop` are also available.
260
+
261
+ Zero or Ciel remains the user-facing teammate. The local coding agent receives
262
+ only the initial relay and active-job Build follow-ups covered by the user’s
263
+ explicit Workshop consent, the assigned contribution branch, and its scoped
264
+ Build Forum—not the raw assistant chat. It
265
+ cannot merge into Main, publish, or use website-management APIs. Lumine records
266
+ the requested and provider-reported model, effort, service tier, runtime/usage
267
+ evidence, coordinator/helper tree, saved artifact, and branch notice. Every
268
+ completed handoff enters the daily integrity flow; probationary, hard-flagged,
269
+ and sampled work requires review. Each unique cleared handoff earns a flat 50
270
+ Karma Points, while retries and helper agents do not multiply the award.
271
+
221
272
  ## Privileged website administration
222
273
 
223
274
  `lumine admin` reuses Mikey's saved CLI login while keeping the visible public
@@ -237,6 +288,13 @@ lumine admin economy trace lock --days 3 \
237
288
  lumine admin rescue wordle-audit --days 30 \
238
289
  --reason "Identify recorded Wordle breaks and rescue status" --json
239
290
  lumine admin daily-run start --identity auto --comment-mode off --json
291
+ lumine admin sponsor applications list --status pending --json
292
+ lumine admin sponsor applications review 12 --decision approve \
293
+ --note "Approved for probationary duty" --json
294
+ lumine admin sponsor integrity scan --json
295
+ lumine admin sponsor integrity cases --status open --json
296
+ lumine admin sponsor integrity get 34 --json
297
+ lumine admin sponsor integrity review 34 --decision clear --json
240
298
  lumine admin todo list --json
241
299
  lumine admin todo add --kind experiment --status in_progress \
242
300
  --title "Validate Zero/Ciel cost optimization" \
@@ -344,7 +402,11 @@ with `todo update`; completing a run does not complete its todos. Experiments
344
402
  must meet their stated acceptance criteria—lower AI cost with weaker user
345
403
  responses is not a successful optimization. `daily-run report` summarizes
346
404
  confirmed mutations, completed queue coverage, explicitly recorded escalations,
347
- unfinished todos, and the run brief before the run is completed.
405
+ unfinished todos, sponsor-integrity state, and the run brief before the run is
406
+ completed. A run cannot complete until its bounded sponsor-integrity snapshot
407
+ has been scanned and every selected case is cleared or disqualified. `hold` and
408
+ `flag` deliberately keep the run open for human judgment; scans never suspend,
409
+ revoke, or disqualify a sponsor automatically.
348
410
 
349
411
  Identity inspection, escalation dispositions, AI-bucket maintenance, and
350
412
  approved Notable User additions and todos are private operator bookkeeping and
package/lib/admin.js CHANGED
@@ -96,9 +96,7 @@ function readBuildReviewContextFile(filePath) {
96
96
  );
97
97
  }
98
98
  const understanding =
99
- typeof parsed.understanding === "string"
100
- ? parsed.understanding.trim()
101
- : "";
99
+ typeof parsed.understanding === "string" ? parsed.understanding.trim() : "";
102
100
  if (!understanding) {
103
101
  throw cliValidationError(
104
102
  "The Build review context understanding must be a non-empty string.",
@@ -816,6 +814,108 @@ export function parseAdminOperation(options) {
816
814
  );
817
815
  }
818
816
 
817
+ if (namespace === "sponsor" || namespace === "sponsors") {
818
+ if (action === "applications") {
819
+ if (!target || target === "list") {
820
+ return readOperation(
821
+ "sponsor.applications.list",
822
+ withQuery("/cli/admin/sponsors/applications", {
823
+ status: options.adminStatus || "pending",
824
+ limit: options.limit,
825
+ }),
826
+ );
827
+ }
828
+ if (target === "review") {
829
+ const applicationId = parseRequiredInteger(
830
+ extra,
831
+ "Sponsor application ID",
832
+ 1,
833
+ );
834
+ const decision = parseChoice(
835
+ options.adminDecision || options.adminStatus,
836
+ "--decision",
837
+ ["approve", "reject"],
838
+ );
839
+ return writeOperation(
840
+ "sponsor.application.review",
841
+ "POST",
842
+ `/cli/admin/sponsors/applications/${applicationId}/review`,
843
+ { decision, note: options.note || undefined },
844
+ );
845
+ }
846
+ }
847
+ if (action === "status" && target === "set") {
848
+ const sponsorUserId = parseRequiredInteger(extra, "Sponsor user ID", 1);
849
+ const status = parseChoice(options.adminStatus, "--status", [
850
+ "probationary",
851
+ "trusted",
852
+ "suspended",
853
+ "revoked",
854
+ ]);
855
+ return writeOperation(
856
+ "sponsor.status.set",
857
+ "PUT",
858
+ `/cli/admin/sponsors/${sponsorUserId}/status`,
859
+ { status, note: options.note || undefined },
860
+ );
861
+ }
862
+ if (action === "integrity") {
863
+ if (target === "status") {
864
+ return readOperation(
865
+ "sponsor.integrity.status",
866
+ "/cli/admin/daily-runs/sponsor-integrity/status",
867
+ );
868
+ }
869
+ if (target === "scan") {
870
+ return writeOperation(
871
+ "sponsor.integrity.scan",
872
+ "POST",
873
+ "/cli/admin/daily-runs/sponsor-integrity/scan",
874
+ {},
875
+ );
876
+ }
877
+ if (target === "cases") {
878
+ return readOperation(
879
+ "sponsor.integrity.cases",
880
+ withQuery("/cli/admin/daily-runs/sponsor-integrity/cases", {
881
+ status: options.adminStatus || "open",
882
+ limit: options.limit,
883
+ }),
884
+ );
885
+ }
886
+ if (target === "get") {
887
+ const caseId = parseRequiredInteger(extra, "Integrity case ID", 1);
888
+ return readOperation(
889
+ "sponsor.integrity.get",
890
+ `/cli/admin/daily-runs/sponsor-integrity/cases/${caseId}`,
891
+ );
892
+ }
893
+ if (target === "review") {
894
+ const caseId = parseRequiredInteger(extra, "Integrity case ID", 1);
895
+ const decision = parseChoice(options.adminDecision, "--decision", [
896
+ "clear",
897
+ "hold",
898
+ "flag",
899
+ "disqualify",
900
+ ]);
901
+ if (decision !== "clear" && !String(options.note || "").trim()) {
902
+ throw cliValidationError(
903
+ "Sponsor-integrity hold, flag, and disqualify decisions require --note <evidence>.",
904
+ );
905
+ }
906
+ return writeOperation(
907
+ "sponsor.integrity.review",
908
+ "POST",
909
+ `/cli/admin/daily-runs/sponsor-integrity/cases/${caseId}/review`,
910
+ { decision, note: options.note || undefined },
911
+ );
912
+ }
913
+ }
914
+ throw cliValidationError(
915
+ "Usage: lumine admin sponsor applications list|review | sponsor status set | sponsor integrity status|scan|cases|get|review.",
916
+ );
917
+ }
918
+
819
919
  if (namespace === "daily-run") {
820
920
  if (action === "start") {
821
921
  return writeOperation(
@@ -1235,6 +1335,35 @@ export function parseAdminOperation(options) {
1235
1335
  );
1236
1336
  }
1237
1337
 
1338
+ if (namespace === "ai-costs") {
1339
+ if (action === "monthly" && !target && !extra) {
1340
+ if (options.adminDays) {
1341
+ throw cliValidationError(
1342
+ "ai-costs monthly uses UTC calendar months and does not accept --days.",
1343
+ );
1344
+ }
1345
+ return readOperation("ai-costs.monthly", "/cli/admin/ai-costs/monthly");
1346
+ }
1347
+ throw cliValidationError("Usage: lumine admin ai-costs monthly [--json].");
1348
+ }
1349
+
1350
+ if (namespace === "media-costs") {
1351
+ if (action === "monthly" && !target && !extra) {
1352
+ if (options.adminDays) {
1353
+ throw cliValidationError(
1354
+ "media-costs monthly uses the canonical UTC ledger and does not accept --days.",
1355
+ );
1356
+ }
1357
+ return readOperation(
1358
+ "media-costs.monthly",
1359
+ "/cli/admin/media-costs/monthly",
1360
+ );
1361
+ }
1362
+ throw cliValidationError(
1363
+ "Usage: lumine admin media-costs monthly [--json].",
1364
+ );
1365
+ }
1366
+
1238
1367
  if (namespace === "brief" && !action) {
1239
1368
  if (options.adminDays) {
1240
1369
  const days = Number(options.adminDays);
@@ -1419,9 +1548,7 @@ export function parseAdminOperation(options) {
1419
1548
  ...(confirmedBuildReviewMethod
1420
1549
  ? { buildReviewMethod: confirmedBuildReviewMethod }
1421
1550
  : {}),
1422
- ...(buildReviewUnderstanding
1423
- ? { buildReviewUnderstanding }
1424
- : {}),
1551
+ ...(buildReviewUnderstanding ? { buildReviewUnderstanding } : {}),
1425
1552
  },
1426
1553
  );
1427
1554
  }
@@ -1465,7 +1592,7 @@ export function parseAdminOperation(options) {
1465
1592
  }
1466
1593
 
1467
1594
  throw cliValidationError(
1468
- "Usage: lumine admin identity|economy|rescue|daily-run|escalation|todo|recommendations|builds|post|subjects|subject|featured|comment|announcement|chat|news|audit|brief|bot-output|notable ...",
1595
+ "Usage: lumine admin identity|economy|rescue|sponsor|daily-run|escalation|todo|recommendations|builds|post|subjects|subject|featured|comment|announcement|chat|news|audit|brief|ai-costs|media-costs|bot-output|notable ...",
1469
1596
  );
1470
1597
  }
1471
1598
 
@@ -2024,6 +2151,16 @@ function parseRequiredInteger(
2024
2151
  return number;
2025
2152
  }
2026
2153
 
2154
+ function parseChoice(value, label, choices) {
2155
+ const normalized = String(value || "")
2156
+ .trim()
2157
+ .toLowerCase();
2158
+ if (!choices.includes(normalized)) {
2159
+ throw cliValidationError(`${label} must be ${choices.join(", ")}.`);
2160
+ }
2161
+ return normalized;
2162
+ }
2163
+
2027
2164
  function defaultDailyRunKey() {
2028
2165
  const parts = new Intl.DateTimeFormat("en-CA", {
2029
2166
  timeZone: "Asia/Bangkok",
@@ -2047,8 +2184,272 @@ function adminValueFingerprint(value) {
2047
2184
  return createHash("sha256").update(JSON.stringify(value)).digest("hex");
2048
2185
  }
2049
2186
 
2187
+ function formatAdminUsd(value) {
2188
+ const amount = Number(value);
2189
+ if (!Number.isFinite(amount)) return "unavailable";
2190
+ return `$${amount.toLocaleString("en-US", {
2191
+ minimumFractionDigits: 2,
2192
+ maximumFractionDigits: 2,
2193
+ })}`;
2194
+ }
2195
+
2196
+ function formatAdminMonthlyCostComparison(projection, previousMonthKey) {
2197
+ const rawPercent = projection?.comparisonToPreviousMonth?.percentChange;
2198
+ const percent = rawPercent === null ? NaN : Number(rawPercent);
2199
+ if (!Number.isFinite(percent)) {
2200
+ return `comparison with ${previousMonthKey} unavailable`;
2201
+ }
2202
+ if (percent === 0) return `even with ${previousMonthKey}`;
2203
+ return `${Math.abs(percent).toFixed(2)}% ${percent < 0 ? "below" : "above"} ${previousMonthKey}`;
2204
+ }
2205
+
2206
+ function printAdminMonthlyAiCosts(monthlyAiCosts) {
2207
+ const previous = monthlyAiCosts.previousMonth;
2208
+ const current = monthlyAiCosts.currentMonth;
2209
+ const generatedAt = new Date(
2210
+ Number(monthlyAiCosts.generatedAt) * 1000,
2211
+ ).toISOString();
2212
+ console.log(`Application AI-cost ledger (UTC; generated ${generatedAt}).`);
2213
+ console.log(
2214
+ `${previous.monthKey} closed month: ${formatAdminUsd(previous.estimatedCostUsd)} estimated cost.`,
2215
+ );
2216
+ if (current.completed.dayCount > 0) {
2217
+ console.log(
2218
+ `${current.monthKey} completed-day MTD through ${current.completed.throughDayKey}: ${formatAdminUsd(current.completed.estimatedCostUsd)} across ${current.completed.dayCount} completed UTC day(s).`,
2219
+ );
2220
+ } else {
2221
+ console.log(
2222
+ `${current.monthKey} completed-day MTD: ${formatAdminUsd(current.completed.estimatedCostUsd)}; no UTC day has completed yet.`,
2223
+ );
2224
+ }
2225
+ console.log(
2226
+ `${current.inProgressDay.dayKey} in progress: ${formatAdminUsd(current.inProgressDay.estimatedCostUsd)} so far (excluded from completed-day MTD and both projections).`,
2227
+ );
2228
+
2229
+ const allPace = current.projections.allCompletedDaysPace;
2230
+ if (allPace) {
2231
+ console.log(
2232
+ `All-completed-days pace full-month projection: ${formatAdminUsd(allPace.estimatedMonthTotalUsd)} (${formatAdminUsd(allPace.dailyAverageUsd)}/day across ${allPace.basisDayCount} completed day(s); ${formatAdminMonthlyCostComparison(allPace, previous.monthKey)}).`,
2233
+ );
2234
+ } else {
2235
+ console.log(
2236
+ "All-completed-days pace full-month projection: unavailable until one UTC day has completed.",
2237
+ );
2238
+ }
2239
+
2240
+ const recentPace = current.projections.recentSevenCompletedDaysPace;
2241
+ if (recentPace) {
2242
+ console.log(
2243
+ `Recent-seven-completed-day pace full-month projection: ${formatAdminUsd(recentPace.estimatedMonthTotalUsd)} (${formatAdminUsd(recentPace.dailyAverageUsd)}/day from ${recentPace.basisStartDayKey} through ${recentPace.basisEndDayKey}; ${formatAdminMonthlyCostComparison(recentPace, previous.monthKey)}).`,
2244
+ );
2245
+ } else {
2246
+ console.log(
2247
+ "Recent-seven-completed-day pace full-month projection: unavailable until seven UTC days have completed.",
2248
+ );
2249
+ }
2250
+ }
2251
+
2252
+ function formatAdminMediaUsd(value) {
2253
+ const amount = Number(value);
2254
+ if (!Number.isFinite(amount)) return "unavailable";
2255
+ return `$${amount.toLocaleString("en-US", {
2256
+ minimumFractionDigits: 2,
2257
+ maximumFractionDigits: 6,
2258
+ })}`;
2259
+ }
2260
+
2261
+ function formatAdminMediaBytes(value) {
2262
+ const bytes = Number(value);
2263
+ if (!Number.isFinite(bytes) || bytes < 0) return "unavailable";
2264
+ if (bytes < 1024) return `${Math.floor(bytes)} B`;
2265
+ const units = ["KB", "MB", "GB", "TB"];
2266
+ let amount = bytes / 1024;
2267
+ let unit = units[0];
2268
+ for (let index = 1; index < units.length && amount >= 1024; index += 1) {
2269
+ amount /= 1024;
2270
+ unit = units[index];
2271
+ }
2272
+ return `${amount.toFixed(amount >= 100 ? 0 : amount >= 10 ? 1 : 2)} ${unit}`;
2273
+ }
2274
+
2275
+ function printAdminMonthlyMediaCosts(monthlyMediaCosts) {
2276
+ const generatedAt = new Date(
2277
+ Number(monthlyMediaCosts.generatedAt) * 1000,
2278
+ ).toISOString();
2279
+ const current = monthlyMediaCosts.currentMonth;
2280
+ const today = monthlyMediaCosts.currentUtcDay;
2281
+ const operations = monthlyMediaCosts.operations;
2282
+ const streamAttempts = today.streamAttempts || {
2283
+ attemptedCount: 0,
2284
+ reachedLiveCount: 0,
2285
+ endedCount: 0,
2286
+ failedCount: 0,
2287
+ cancelledCount: 0,
2288
+ inProgressCount: 0,
2289
+ failureCodeCounts: [],
2290
+ };
2291
+ console.log(
2292
+ `Lumine media-cost monitor (UTC; ${monthlyMediaCosts.status}; generated ${generatedAt}).`,
2293
+ );
2294
+ console.log(
2295
+ `${current.monthKey}: ${formatAdminMediaUsd(current.estimatedSpentUsd)} settled estimate + ${formatAdminMediaUsd(current.activeReservedUsd)} active reservations + ${formatAdminMediaUsd(current.carryoverUsd)} carryover = ${formatAdminMediaUsd(current.guardedTotalUsd)} guarded of ${formatAdminMediaUsd(current.limitUsd)} (${Number(current.percentUsed).toFixed(2)}% used; ${formatAdminMediaUsd(current.remainingUsd)} remaining).`,
2296
+ );
2297
+ console.log(
2298
+ `${today.dayKey} so far: ${today.reservationsCreated} action(s) reserved, ${today.commitmentsSettled} committed, ${today.cancellationsSettled} cancelled, ${formatAdminMediaUsd(today.estimatedCostSettledUsd)} settled estimate.`,
2299
+ );
2300
+ console.log(
2301
+ `Stream attempts created ${today.dayKey} UTC: ${streamAttempts.attemptedCount} attempted / ${streamAttempts.reachedLiveCount} reached live / ${streamAttempts.endedCount} ended after live / ${streamAttempts.failedCount} failed / ${streamAttempts.cancelledCount} cancelled before live / ${streamAttempts.inProgressCount} still in progress.`,
2302
+ );
2303
+ const streamFailureCodeCounts = Array.isArray(
2304
+ streamAttempts.failureCodeCounts,
2305
+ )
2306
+ ? streamAttempts.failureCodeCounts
2307
+ : [];
2308
+ console.log(
2309
+ streamFailureCodeCounts.length > 0
2310
+ ? `Stream failure codes: ${streamFailureCodeCounts
2311
+ .map((entry) => `${entry.code}=${entry.count}`)
2312
+ .join(", ")}.`
2313
+ : "Stream failure codes: none.",
2314
+ );
2315
+ const stillActiveOrCleanupPendingCount =
2316
+ operations.live.stillActiveOrCleanupPendingCount ??
2317
+ Number(operations.live.provisioningCount || 0) +
2318
+ Number(operations.live.readyCount || 0) +
2319
+ Number(operations.live.liveCount || 0) +
2320
+ Number(operations.live.endingCount || 0) +
2321
+ Number(operations.live.cleanupFailedCount || 0);
2322
+ const replayOperations = operations.replays || {
2323
+ pendingCount: 0,
2324
+ processingCount: 0,
2325
+ readyCount: 0,
2326
+ failedCount: 0,
2327
+ deletePendingCount: 0,
2328
+ deleteFailedCount: 0,
2329
+ expiredReadyCount: 0,
2330
+ finalizationOverdueCount: 0,
2331
+ deletionOverdueCount: 0,
2332
+ storedBytes: 0,
2333
+ storedObjectCount: 0,
2334
+ };
2335
+ const replayViewers = operations.replayViewers || {
2336
+ activeGrantCount: 0,
2337
+ expiredActiveGrantCount: 0,
2338
+ };
2339
+ const kindRows = [
2340
+ ["Short clips", current.byKind.clip],
2341
+ ["Live inputs", current.byKind.liveInput],
2342
+ ["Live viewers", current.byKind.liveViewer],
2343
+ ];
2344
+ if (current.byKind.replayViewer) {
2345
+ kindRows.push(["Replay viewers", current.byKind.replayViewer]);
2346
+ }
2347
+ for (const [label, cost] of kindRows) {
2348
+ console.log(
2349
+ `${label}: ${cost.actionCount} action(s), ${cost.committedCount} committed for ${formatAdminMediaUsd(cost.estimatedSpentUsd)}, ${cost.activeReservedCount} active reservation(s) holding ${formatAdminMediaUsd(cost.activeReservedUsd)}, ${cost.cancelledCount} cancelled.`,
2350
+ );
2351
+ }
2352
+ console.log(
2353
+ `Ledger reconciliation: ${current.reconciliation.consistent ? "consistent" : "MISMATCH"}; spent delta ${formatAdminMediaUsd(current.reconciliation.spentDeltaUsd)}, reserved delta ${formatAdminMediaUsd(current.reconciliation.reservedDeltaUsd)}.`,
2354
+ );
2355
+ console.log(
2356
+ `Operations: clips ${operations.clips.completingCount} completing / ${operations.clips.processingCount} processing / ${operations.clips.staleCount} stale; live ${operations.live.liveCount} broadcasting / ${stillActiveOrCleanupPendingCount} active-or-cleanup-pending / ${operations.live.costBearingChannelCount} cost-bearing channel(s) / ${operations.live.possibleOrphanedCount ?? operations.live.cleanupOverdueCount} possible orphan(s); viewers ${operations.viewers.activeGrantCount} active / ${operations.viewers.expiredActiveGrantCount} expired-active.`,
2357
+ );
2358
+ console.log(
2359
+ `Replays: ${replayOperations.pendingCount} pending / ${replayOperations.processingCount} processing / ${replayOperations.readyCount} ready / ${replayOperations.failedCount} failed / ${replayOperations.deletePendingCount} deleting / ${replayOperations.deleteFailedCount} delete-failed; ${replayOperations.finalizationOverdueCount} finalization-overdue / ${replayOperations.deletionOverdueCount} deletion-overdue / ${replayOperations.expiredReadyCount} expired-ready; ${formatAdminMediaBytes(replayOperations.storedBytes)} across ${replayOperations.storedObjectCount} canonical object(s); viewers ${replayViewers.activeGrantCount} active / ${replayViewers.expiredActiveGrantCount} expired-active.`,
2360
+ );
2361
+ console.log(
2362
+ `Shared runtime storage context: ${operations.runtimeStorage.readyImages.assetCount} ready image(s), ${formatAdminMediaBytes(operations.runtimeStorage.readyImages.totalBytes)}; ${operations.runtimeStorage.readyClips.assetCount} ready clip(s), ${formatAdminMediaBytes(operations.runtimeStorage.readyClips.totalBytes)}. Images include all Build runtime image uploads, not only camera captures.`,
2363
+ );
2364
+ if (monthlyMediaCosts.alerts.length === 0) {
2365
+ console.log("Media-cost alerts: none.");
2366
+ } else {
2367
+ for (const alert of monthlyMediaCosts.alerts) {
2368
+ console.log(
2369
+ `Media-cost alert [${String(alert.severity).toUpperCase()}] ${alert.code}: ${alert.message}`,
2370
+ );
2371
+ }
2372
+ }
2373
+ console.log(
2374
+ "These are conservative provider-cost ledger estimates, not an AWS invoice; reconcile IVS, MediaConvert, replay S3, and shared runtime S3 Cost Explorer data separately after billing lag.",
2375
+ );
2376
+ }
2377
+
2050
2378
  function printAdminResult({ operation, result }) {
2051
2379
  const data = result?.data || {};
2380
+ if (Array.isArray(data.applications)) {
2381
+ console.log(`${data.applications.length} sponsor application(s):`);
2382
+ for (const application of data.applications) {
2383
+ console.log(
2384
+ ` #${application.id} ${String(application.status).toUpperCase()} · ${application.username || `user ${application.userId}`} · agreement ${application.agreementVersion || "unknown"}`,
2385
+ );
2386
+ }
2387
+ return;
2388
+ }
2389
+ if (data.application) {
2390
+ console.log(
2391
+ `Sponsor application #${data.application.id}: ${String(data.application.status).toUpperCase()} · ${data.application.username || `user ${data.application.userId}`}`,
2392
+ );
2393
+ return;
2394
+ }
2395
+ if (data.sponsor) {
2396
+ console.log(
2397
+ `Sponsor ${data.sponsor.username || `user ${data.sponsor.userId}`}: ${String(data.sponsor.status).toUpperCase()}.`,
2398
+ );
2399
+ return;
2400
+ }
2401
+ if (data.scan) {
2402
+ const scan = data.scan;
2403
+ console.log(
2404
+ `Sponsor-integrity scan #${scan.id || "not started"}: ${scan.status || "not started"} · ${scan.scannedCount || 0} scanned · ${scan.selectedReviewCount || 0} selected · ${data.openCaseCount || 0} open.`,
2405
+ );
2406
+ if (Array.isArray(data.casesCreated) && data.casesCreated.length > 0) {
2407
+ console.log(
2408
+ `Created ${data.casesCreated.length} review case(s) on this page.`,
2409
+ );
2410
+ }
2411
+ return;
2412
+ }
2413
+ if (Array.isArray(data.cases)) {
2414
+ console.log(`${data.cases.length} sponsor-integrity case(s):`);
2415
+ for (const item of data.cases) {
2416
+ const flags =
2417
+ Array.isArray(item.hardFlags) && item.hardFlags.length
2418
+ ? ` · flags=${item.hardFlags.join(",")}`
2419
+ : "";
2420
+ console.log(
2421
+ ` #${item.id} ${String(item.status).toUpperCase()} · job #${item.jobId} · ${item.sponsorUsername || `sponsor ${item.sponsorUserId}`}${flags}`,
2422
+ );
2423
+ }
2424
+ return;
2425
+ }
2426
+ if (data.case) {
2427
+ const item = data.case;
2428
+ console.log(
2429
+ `Sponsor-integrity case #${item.id}: ${String(item.status).toUpperCase()} · job #${item.job?.id} · ${item.sponsorUsername || `sponsor ${item.sponsorUserId}`}.`,
2430
+ );
2431
+ const relays = Array.isArray(item.relays) && item.relays.length > 0
2432
+ ? item.relays
2433
+ : item.relay
2434
+ ? [item.relay]
2435
+ : [];
2436
+ console.log(
2437
+ `Relays: ${relays.map((relay) => `${relay.kind || "initial_request"}: ${relay.summary || "(missing)"}`).join(" | ") || "(missing)"}`,
2438
+ );
2439
+ console.log(
2440
+ `Provenance: ${(item.agents || []).map((agent) => `${agent.role}:${agent.provider}:${agent.resolvedModel || "unresolved"}:${agent.resolvedEffort || "unresolved"}`).join(" · ") || "missing"}`,
2441
+ );
2442
+ console.log(
2443
+ `Artifact files: ${(item.artifactFiles || []).map((file) => file.path).join(", ") || "none"}`,
2444
+ );
2445
+ return;
2446
+ }
2447
+ if (data.caseId) {
2448
+ console.log(
2449
+ `Sponsor-integrity case #${data.caseId}: ${data.decision}${data.scanCompleted ? "; scan complete" : ""}.`,
2450
+ );
2451
+ return;
2452
+ }
2052
2453
  if (data.review) {
2053
2454
  console.log(
2054
2455
  `Confirmed Build #${data.review.buildId} runtime at published artifact #${data.review.publishedArtifactVersionId}.`,
@@ -2057,6 +2458,14 @@ function printAdminResult({ operation, result }) {
2057
2458
  console.log(`Review receipt: ${data.receiptPath}`);
2058
2459
  return;
2059
2460
  }
2461
+ if (data.monthlyAiCosts) {
2462
+ printAdminMonthlyAiCosts(data.monthlyAiCosts);
2463
+ return;
2464
+ }
2465
+ if (data.monthlyMediaCosts) {
2466
+ printAdminMonthlyMediaCosts(data.monthlyMediaCosts);
2467
+ return;
2468
+ }
2060
2469
  if (data.validation) {
2061
2470
  console.log(
2062
2471
  `Editorial valid for edition #${data.validation.editionId}: ${data.validation.citedEventCount} cited and ${data.validation.coveredEventCount} covered event(s).`,
@@ -2089,6 +2498,11 @@ function printAdminResult({ operation, result }) {
2089
2498
  );
2090
2499
  }
2091
2500
  printTodoItems(report.carryoverTodos?.items || [], "Unfinished work");
2501
+ if (report.sponsorIntegrity) {
2502
+ console.log(
2503
+ `Sponsor integrity: ${report.sponsorIntegrity.scan?.status || "not started"}; ${report.sponsorIntegrity.cases?.open || 0} open case(s); ${report.sponsorIntegrity.pendingApplications || 0} pending application(s).`,
2504
+ );
2505
+ }
2092
2506
  const surfaces = report.brief?.engagementPulse?.surfaces;
2093
2507
  if (surfaces && typeof surfaces === "object") {
2094
2508
  const deltas = Object.entries(surfaces)