@stage5/lumine 0.2.67 → 0.2.69
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 +26 -1
- package/lib/admin-featured-history.js +185 -0
- package/lib/admin-featured.js +559 -0
- package/lib/admin-runtime-logs.js +23 -3
- package/lib/admin-workflows.js +54 -4
- package/lib/admin.js +371 -101
- package/lib/commands.js +19 -8
- package/lib/constants.js +21 -0
- package/lib/sdk.js +6 -0
- package/lib/sponsor-duty.js +15 -2
- package/package.json +1 -1
- package/sdk/BUILD_SDK_INDEX.md +68 -6
- package/sdk/LUMINE_ADMIN.md +542 -48
package/README.md
CHANGED
|
@@ -387,6 +387,17 @@ lumine admin subject unfeature 123 --json
|
|
|
387
387
|
lumine admin featured reorder --subject-ids 30,20,10 --json
|
|
388
388
|
lumine admin featured rotate --remove-subject-ids 30,20 \
|
|
389
389
|
--add-subject-ids 50,40 --json
|
|
390
|
+
lumine admin featured plan --remove-subject-ids 30,20 \
|
|
391
|
+
--add-subject-ids 50,40 --subject-ids 40,50,10 \
|
|
392
|
+
--posted-after 2026-09-01T00:00:00+07:00 --output featured-plan.json --json
|
|
393
|
+
# Only after Mikey approves the exact mapping and final order:
|
|
394
|
+
lumine admin featured apply --file featured-plan.json --approve <exact-plan-hash> --json
|
|
395
|
+
lumine admin featured comments scan --checkpoint featured-read.json --json
|
|
396
|
+
# Read every page file before acknowledging; downloads are not reviews.
|
|
397
|
+
lumine admin featured comments acknowledge --checkpoint featured-read.json --reviewed --json
|
|
398
|
+
lumine admin featured comments recommend --file featured-decisions.json \
|
|
399
|
+
--checkpoint featured-recommend.json --json
|
|
400
|
+
lumine admin featured comments report --checkpoint featured-read.json --json
|
|
390
401
|
lumine admin brief --days 3 --json
|
|
391
402
|
lumine admin notable add Minecrarft_guy --note "Created 8 thoughtful subjects and helped peers in 23 comments this window." --json
|
|
392
403
|
lumine admin post recommend comment:456 --anyone-can-reward --reward-twinkles 3 --json
|
|
@@ -418,7 +429,9 @@ Zero/Ciel responder without Lumine remaining active.
|
|
|
418
429
|
|
|
419
430
|
`--scope full` is the default and authorizes the complete daily-management
|
|
420
431
|
workflow. `--scope featured` is a deliberately narrow run for a requested
|
|
421
|
-
Featured-only slice: it
|
|
432
|
+
Featured-only slice: it can review and encourage snapshot-bound Featured
|
|
433
|
+
comments, but cannot publish comments or use generic recommendation/reward
|
|
434
|
+
commands. It does not surface carry-over todos, does
|
|
422
435
|
not write full-run queue coverage, does not require sponsor review, and never
|
|
423
436
|
advances full-run review windows. Its dedicated start endpoint also prevents an
|
|
424
437
|
older API from silently interpreting it as a full run. The
|
|
@@ -427,6 +440,18 @@ the server to prove both a strict posting-date boundary and complete
|
|
|
427
440
|
never-Featured history. `featured history` returns that evidence without the
|
|
428
441
|
large repeated board snapshots in the general audit trail.
|
|
429
442
|
|
|
443
|
+
`featured plan`/`apply` bind approval to the exact server-stored board and
|
|
444
|
+
history revision; all approved replacements and ordering commit together.
|
|
445
|
+
Equal-size swaps and reorders support the website's 100-Subject capacity while
|
|
446
|
+
delegated growth remains capped at 20. The CLI verifies the final live order.
|
|
447
|
+
`featured comments scan` walks every thread (including old/nested replies),
|
|
448
|
+
with private, hashed pages and `--resume` recovery. `acknowledge --reviewed`
|
|
449
|
+
records actual reading separately from downloading. `recommend` applies only
|
|
450
|
+
agent-selected comments from acknowledged threads, with reward eligibility off
|
|
451
|
+
by default, and resumes the exact decision file/checkpoint safely. See
|
|
452
|
+
[the admin contract](sdk/LUMINE_ADMIN.md#exact-approved-refresh-and-resumable-comment-encouragement)
|
|
453
|
+
for the decision-file format, partial-coverage rules, and deployment prerequisites.
|
|
454
|
+
|
|
430
455
|
Management agents also inspect recent public Build candidates during each full
|
|
431
456
|
daily review.
|
|
432
457
|
`builds review` opens one published app in an isolated temporary Chromium
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { readAdminJsonFile, writeAdminJsonFile } from "./admin-news.js";
|
|
6
|
+
import {
|
|
7
|
+
acquireCheckpointLock,
|
|
8
|
+
releaseCheckpointLock,
|
|
9
|
+
runAutomaticPagination,
|
|
10
|
+
combinePaginatedResults,
|
|
11
|
+
writePaginatedResultFile,
|
|
12
|
+
} from "./admin-workflows.js";
|
|
13
|
+
|
|
14
|
+
// API read bound, deliberately unrelated to the delegated addition policy.
|
|
15
|
+
export const FEATURED_HISTORY_BATCH_SIZE = 100;
|
|
16
|
+
|
|
17
|
+
function invalid(message) {
|
|
18
|
+
const error = new Error(message);
|
|
19
|
+
error.code = "CLI_ADMIN_CLI_VALIDATION";
|
|
20
|
+
return error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function runBatchedFeaturedHistory(args) {
|
|
24
|
+
const { options, operation, runId } = args;
|
|
25
|
+
if (options.adminCursor) {
|
|
26
|
+
throw invalid("Use --resume with --all, not a single-batch --cursor.");
|
|
27
|
+
}
|
|
28
|
+
const subjectIds = operation.pagination.filters.subjectIds;
|
|
29
|
+
const fingerprint = createHash("sha256")
|
|
30
|
+
.update(
|
|
31
|
+
JSON.stringify({
|
|
32
|
+
version: 1,
|
|
33
|
+
runId,
|
|
34
|
+
apiUrl: options.apiUrl,
|
|
35
|
+
path: operation.path,
|
|
36
|
+
}),
|
|
37
|
+
)
|
|
38
|
+
.digest("hex");
|
|
39
|
+
const checkpointPath = path.resolve(
|
|
40
|
+
options.adminCheckpoint ||
|
|
41
|
+
path.join(
|
|
42
|
+
os.tmpdir(),
|
|
43
|
+
`lumine-admin-run-${runId}-featured-history-${fingerprint}.json`,
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
const outputPath = options.adminOutput
|
|
47
|
+
? path.resolve(options.adminOutput)
|
|
48
|
+
: null;
|
|
49
|
+
if (
|
|
50
|
+
outputPath === checkpointPath ||
|
|
51
|
+
outputPath?.startsWith(`${checkpointPath}.`)
|
|
52
|
+
) {
|
|
53
|
+
throw invalid(
|
|
54
|
+
"History output must not overwrite its checkpoint or batch files.",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
const lock = acquireCheckpointLock(checkpointPath, fingerprint);
|
|
58
|
+
try {
|
|
59
|
+
let generation;
|
|
60
|
+
if (options.adminResume) {
|
|
61
|
+
const saved = readAdminJsonFile(
|
|
62
|
+
checkpointPath,
|
|
63
|
+
"the Featured history batch checkpoint",
|
|
64
|
+
);
|
|
65
|
+
if (
|
|
66
|
+
saved.kind !== "featured-history-batches" ||
|
|
67
|
+
saved.fingerprint !== fingerprint ||
|
|
68
|
+
!/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(
|
|
69
|
+
saved.generation || "",
|
|
70
|
+
)
|
|
71
|
+
) {
|
|
72
|
+
throw invalid(
|
|
73
|
+
"The checkpoint does not belong to this run and exact history request.",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
generation = saved.generation;
|
|
77
|
+
} else {
|
|
78
|
+
generation = randomUUID();
|
|
79
|
+
writeAdminJsonFile(
|
|
80
|
+
checkpointPath,
|
|
81
|
+
{ kind: "featured-history-batches", fingerprint, generation },
|
|
82
|
+
{ privateFile: true },
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
const results = [];
|
|
86
|
+
for (
|
|
87
|
+
let offset = 0;
|
|
88
|
+
offset < subjectIds.length;
|
|
89
|
+
offset += FEATURED_HISTORY_BATCH_SIZE
|
|
90
|
+
) {
|
|
91
|
+
const ids = subjectIds.slice(
|
|
92
|
+
offset,
|
|
93
|
+
offset + FEATURED_HISTORY_BATCH_SIZE,
|
|
94
|
+
);
|
|
95
|
+
const batchIndex = offset / FEATURED_HISTORY_BATCH_SIZE;
|
|
96
|
+
const batchCheckpoint = `${checkpointPath}.${generation}.batch-${batchIndex}.json`;
|
|
97
|
+
const url = new URL(operation.path, "https://lumine.invalid");
|
|
98
|
+
url.searchParams.set("subjectIds", ids.join(","));
|
|
99
|
+
const result = await runAutomaticPagination({
|
|
100
|
+
...args,
|
|
101
|
+
options: {
|
|
102
|
+
...options,
|
|
103
|
+
adminOutput: undefined,
|
|
104
|
+
adminCheckpoint: batchCheckpoint,
|
|
105
|
+
adminResume: !!options.adminResume && existsSync(batchCheckpoint),
|
|
106
|
+
},
|
|
107
|
+
operation: {
|
|
108
|
+
...operation,
|
|
109
|
+
path: `${url.pathname}${url.search}`,
|
|
110
|
+
pagination: { ...operation.pagination, filters: { subjectIds: ids } },
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
if (
|
|
114
|
+
JSON.stringify(result.data.subjects?.map((subject) => subject.id)) !==
|
|
115
|
+
JSON.stringify(ids)
|
|
116
|
+
) {
|
|
117
|
+
throw invalid(
|
|
118
|
+
"The API did not return a canonical summary for every requested subject.",
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
results.push(result);
|
|
122
|
+
}
|
|
123
|
+
const firstCoverage = results[0].data.coverage;
|
|
124
|
+
const uniformCoverage = results.every(
|
|
125
|
+
(result) =>
|
|
126
|
+
result.data.coverage?.complete === firstCoverage.complete &&
|
|
127
|
+
result.data.coverage?.startedAt === firstCoverage.startedAt,
|
|
128
|
+
);
|
|
129
|
+
const result = combinePaginatedResults(
|
|
130
|
+
{
|
|
131
|
+
ok: true,
|
|
132
|
+
status: "success",
|
|
133
|
+
data: {
|
|
134
|
+
coverage: uniformCoverage
|
|
135
|
+
? firstCoverage
|
|
136
|
+
: { complete: false, startedAt: null },
|
|
137
|
+
subjects: results.flatMap((result) => result.data.subjects),
|
|
138
|
+
pagination: {
|
|
139
|
+
nextCursor: null,
|
|
140
|
+
hasMore: false,
|
|
141
|
+
exhausted: true,
|
|
142
|
+
snapshotMaxId: null,
|
|
143
|
+
snapshotTimeStamp: null,
|
|
144
|
+
after: null,
|
|
145
|
+
snapshotScope: "per-batch",
|
|
146
|
+
eventOrder: "subject-batch-then-id-descending",
|
|
147
|
+
},
|
|
148
|
+
scan: {
|
|
149
|
+
checkpointPath,
|
|
150
|
+
resumed: !!options.adminResume,
|
|
151
|
+
pages: results.reduce(
|
|
152
|
+
(sum, result) => sum + result.data.scan.pages,
|
|
153
|
+
0,
|
|
154
|
+
),
|
|
155
|
+
scannedCount: results.reduce(
|
|
156
|
+
(sum, result) => sum + result.data.scan.scannedCount,
|
|
157
|
+
0,
|
|
158
|
+
),
|
|
159
|
+
candidateCount: results.reduce(
|
|
160
|
+
(sum, result) => sum + result.data.scan.candidateCount,
|
|
161
|
+
0,
|
|
162
|
+
),
|
|
163
|
+
batches: results.map((result, index) => ({
|
|
164
|
+
subjectIds: subjectIds.slice(
|
|
165
|
+
index * FEATURED_HISTORY_BATCH_SIZE,
|
|
166
|
+
(index + 1) * FEATURED_HISTORY_BATCH_SIZE,
|
|
167
|
+
),
|
|
168
|
+
coverage: result.data.coverage,
|
|
169
|
+
pagination: result.data.pagination,
|
|
170
|
+
scan: result.data.scan,
|
|
171
|
+
})),
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
results,
|
|
176
|
+
);
|
|
177
|
+
if (outputPath) {
|
|
178
|
+
result.data.scan.outputPath = outputPath;
|
|
179
|
+
await writePaginatedResultFile(outputPath, result);
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
} finally {
|
|
183
|
+
releaseCheckpointLock(lock);
|
|
184
|
+
}
|
|
185
|
+
}
|