@stage5/lumine 0.2.26 → 0.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/admin.js +54 -1
- package/lib/commands.js +3 -0
- package/package.json +1 -1
- package/sdk/LUMINE_ADMIN.md +103 -26
package/README.md
CHANGED
|
@@ -181,6 +181,7 @@ lumine admin subject feature 123 --json
|
|
|
181
181
|
lumine admin subject unfeature 123 --json
|
|
182
182
|
lumine admin featured reorder --subject-ids 30,20,10 --json
|
|
183
183
|
lumine admin brief --days 3 --json
|
|
184
|
+
lumine admin notable add Minecrarft_guy --json
|
|
184
185
|
lumine admin post recommend comment:456 --anyone-can-reward --reward-twinkles 3 --json
|
|
185
186
|
lumine admin post reward comment:456 --twinkles 3 --json
|
|
186
187
|
lumine admin daily-run complete --json
|
package/lib/admin.js
CHANGED
|
@@ -12,6 +12,11 @@ const MAX_COMPOSED_COMMENT_LENGTH = 10_000;
|
|
|
12
12
|
// its model and no AI Energy is spent.
|
|
13
13
|
function readComposedCommentFile(filePath) {
|
|
14
14
|
const normalizedPath = String(filePath || "").trim();
|
|
15
|
+
if (!normalizedPath) {
|
|
16
|
+
throw cliValidationError(
|
|
17
|
+
"Pass composed comment text with --file <comment.md>.",
|
|
18
|
+
);
|
|
19
|
+
}
|
|
15
20
|
let contents;
|
|
16
21
|
try {
|
|
17
22
|
contents = readFileSync(normalizedPath, "utf8");
|
|
@@ -580,6 +585,24 @@ export function parseAdminOperation(options) {
|
|
|
580
585
|
);
|
|
581
586
|
}
|
|
582
587
|
|
|
588
|
+
if (namespace === "notable" && action === "add") {
|
|
589
|
+
const rawTarget = String(target || "").trim();
|
|
590
|
+
if (!rawTarget) {
|
|
591
|
+
throw cliValidationError(
|
|
592
|
+
"Usage: lumine admin notable add <userId|username>.",
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
const body = /^\d+$/.test(rawTarget)
|
|
596
|
+
? { userId: parseRequiredInteger(rawTarget, "user ID", 1) }
|
|
597
|
+
: { username: rawTarget };
|
|
598
|
+
return writeOperation(
|
|
599
|
+
"notable.add",
|
|
600
|
+
"POST",
|
|
601
|
+
"/cli/admin/notable-users",
|
|
602
|
+
body,
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
|
|
583
606
|
if (namespace === "brief" && !action) {
|
|
584
607
|
if (options.adminDays) {
|
|
585
608
|
const days = Number(options.adminDays);
|
|
@@ -638,6 +661,30 @@ export function parseAdminOperation(options) {
|
|
|
638
661
|
},
|
|
639
662
|
);
|
|
640
663
|
}
|
|
664
|
+
if (action === "edit") {
|
|
665
|
+
const rawTarget = String(target || "").trim();
|
|
666
|
+
let commentId;
|
|
667
|
+
if (/^\d+$/.test(rawTarget)) {
|
|
668
|
+
commentId = parseRequiredInteger(rawTarget, "comment id", 1);
|
|
669
|
+
} else {
|
|
670
|
+
const parsedTarget = parseRecommendationTarget({
|
|
671
|
+
target,
|
|
672
|
+
explicitType: options.adminType,
|
|
673
|
+
});
|
|
674
|
+
if (parsedTarget.type !== "comment") {
|
|
675
|
+
throw cliValidationError(
|
|
676
|
+
"comment edit targets a comment: lumine admin comment edit <commentId> --file <comment.md>.",
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
commentId = parsedTarget.id;
|
|
680
|
+
}
|
|
681
|
+
return writeOperation(
|
|
682
|
+
"comment.edit",
|
|
683
|
+
"PUT",
|
|
684
|
+
`/cli/admin/comments/${commentId}`,
|
|
685
|
+
{ content: readComposedCommentFile(options.adminFile) },
|
|
686
|
+
);
|
|
687
|
+
}
|
|
641
688
|
if (action === "post") {
|
|
642
689
|
const draftId = parseRequiredInteger(
|
|
643
690
|
options.draftId || target,
|
|
@@ -654,7 +701,7 @@ export function parseAdminOperation(options) {
|
|
|
654
701
|
}
|
|
655
702
|
|
|
656
703
|
throw cliValidationError(
|
|
657
|
-
"Usage: lumine admin identity|daily-run|recommendations|post|subjects|subject|featured|comment|news|audit|brief ...",
|
|
704
|
+
"Usage: lumine admin identity|daily-run|recommendations|post|subjects|subject|featured|comment|news|audit|brief|notable ...",
|
|
658
705
|
);
|
|
659
706
|
}
|
|
660
707
|
|
|
@@ -1059,6 +1106,12 @@ function printAdminResult({ operation, result }) {
|
|
|
1059
1106
|
}
|
|
1060
1107
|
return;
|
|
1061
1108
|
}
|
|
1109
|
+
if (data.notableUser) {
|
|
1110
|
+
console.log(
|
|
1111
|
+
`${result.status || "success"}: ${data.notableUser.username || "unknown"} (#${data.notableUser.userId}).`,
|
|
1112
|
+
);
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1062
1115
|
if (data.skip) {
|
|
1063
1116
|
console.log(
|
|
1064
1117
|
`${result.status}: ${data.skip.contentType}:${data.skip.contentId} skipped.`,
|
package/lib/commands.js
CHANGED
|
@@ -2526,7 +2526,9 @@ export function printHelp() {
|
|
|
2526
2526
|
lumine admin comment draft <target> [--type subject|comment|aiStory|dailyReflection] [--identity zero|ciel|auto] [--json]
|
|
2527
2527
|
lumine admin comment reply comment:<id> [--identity zero|ciel|auto] [--json]
|
|
2528
2528
|
lumine admin comment post --draft-id <id> [--json]
|
|
2529
|
+
lumine admin comment edit <comment-id> --file <comment.md> [--json]
|
|
2529
2530
|
lumine admin brief [--days <1..30>] [--json]
|
|
2531
|
+
lumine admin notable add <user-id|username> [--json]
|
|
2530
2532
|
lumine admin audit [list] [--run current|last|<run-id>] [--target <target>] [--actions <a,b>] [--full] [--cursor <cursor>] [--json]
|
|
2531
2533
|
|
|
2532
2534
|
Examples:
|
|
@@ -2607,6 +2609,7 @@ Options:
|
|
|
2607
2609
|
--reward-twinkles 3 Pair a recommendation with exactly 3 Twinkles
|
|
2608
2610
|
--twinkles 3 Give exactly 3 Twinkles through the normal economy
|
|
2609
2611
|
--draft-id <id> Canonical delegated comment draft ID
|
|
2612
|
+
--file <path> Editorial JSON or composed comment text file
|
|
2610
2613
|
--reason <text> Reason when marking an admin run failed
|
|
2611
2614
|
--force Overwrite server files even if this workspace is stale or missing filesHash
|
|
2612
2615
|
--search <text> Search public open-source Builds
|
package/package.json
CHANGED
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -1041,6 +1041,8 @@ type NewsSubmit = NewsStatus; // "success"; newspaper includes revisionNumber
|
|
|
1041
1041
|
```bash
|
|
1042
1042
|
lumine admin brief --json
|
|
1043
1043
|
lumine admin brief --days 3 --json
|
|
1044
|
+
lumine admin notable add 12647 --json
|
|
1045
|
+
lumine admin notable add Minecrarft_guy --json
|
|
1044
1046
|
```
|
|
1045
1047
|
|
|
1046
1048
|
Read-only management insights for the delegated workflow, windowed since the
|
|
@@ -1062,24 +1064,39 @@ Four sections (Mikey's chosen cut, 2026-08-10):
|
|
|
1062
1064
|
- `aiSpending` — a compact projection of the management AI-cost report:
|
|
1063
1065
|
`summary`, top spending accounts, top risk groups. Unlike the other sections'
|
|
1064
1066
|
exact `window.sinceTs`, this existing report is bucketed into whole UTC days;
|
|
1065
|
-
`aiSpending.
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1067
|
+
`aiSpending.startDayIndex` and `endDayIndex` are its canonical bounds. The
|
|
1068
|
+
report period can begin up to one day before or after the exact brief window,
|
|
1069
|
+
so use those bounds when describing it. Flag accounts that jumped tiers or
|
|
1070
|
+
dominate that report period. May be `{ unavailable: true }` if the cost
|
|
1071
|
+
report fails; say so rather than guessing.
|
|
1069
1072
|
- `notableCandidates` — kids (never bots, staff `userType`s, or users already
|
|
1070
1073
|
on the Notable Users list) ranked by authored activity in the window, with
|
|
1071
1074
|
`isNewUser` marking window-new signups. Use it to find the overlooked and
|
|
1072
1075
|
rising users the editorial priorities exist for, and propose additions to
|
|
1073
|
-
Mikey's Notable Users list in the report
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1076
|
+
Mikey's Notable Users list in the report. When Mikey approves additions,
|
|
1077
|
+
execute them with `lumine admin notable add <userId|username>` (idempotent —
|
|
1078
|
+
an existing member returns `already_done`; requires the `notable:write`
|
|
1079
|
+
scope, audited as `notable.add`, and writes through the management page's
|
|
1080
|
+
own canonical service). Without his approval the run only proposes.
|
|
1081
|
+
- `teachers` — the mentor/sage achievement holders (the accounts the website
|
|
1082
|
+
titles teacher/headteacher): real classroom teachers, NOT the
|
|
1083
|
+
`userType='supermod'` Korean operations staff, whose work-only usage is
|
|
1084
|
+
expected and deliberately excluded. Mikey's standing question here: which
|
|
1085
|
+
teachers genuinely use the website for themselves and which only work
|
|
1086
|
+
through it. Recommendations and rewards are classroom "work" verbs and
|
|
1087
|
+
count for nothing toward interest; genuine personal interest is measured by
|
|
1088
|
+
what a teacher does for themselves — authored subjects/comments,
|
|
1089
|
+
`reflections` (Daily Reflections answered), `dailyTasksCompleted`,
|
|
1090
|
+
`wordlePlays`, `buildsTouched` (Lumine builds they own that changed in the
|
|
1091
|
+
window), `aiStories`, and bounded `xpEvents` (XP-ledger activity). Shape:
|
|
1092
|
+
`genuinelyInterested` (interestScore > 0, ordered by it, each row carrying
|
|
1093
|
+
all the per-window counters plus `rank: mentor|sage` and `workScore`),
|
|
1094
|
+
`workOnly` (classroom verbs only, zero personal signals), `activeButSilent`
|
|
1095
|
+
(window-active log-ins with nothing at all, capped at 40), and `totals`.
|
|
1096
|
+
Daily-task counts use canonical whole-day indices and can begin up to one
|
|
1097
|
+
calendar day before the exact `window.sinceTs`.
|
|
1098
|
+
Report the contrast between the buckets, and treat `genuinelyInterested` as
|
|
1099
|
+
notable-users-but-for-teachers.
|
|
1083
1100
|
|
|
1084
1101
|
The command needs only an active run's `content:read` scope and mutates
|
|
1085
1102
|
nothing; reading the brief is not audited content action. Window boundaries
|
|
@@ -1088,6 +1105,27 @@ on the big append-only ledgers are found by binary-searching the PRIMARY key
|
|
|
1088
1105
|
is still bounded to the selected 1–30 day window.
|
|
1089
1106
|
|
|
1090
1107
|
```ts
|
|
1108
|
+
type TeacherInsight = {
|
|
1109
|
+
userId: number;
|
|
1110
|
+
username: string | null;
|
|
1111
|
+
rank: 'mentor' | 'sage';
|
|
1112
|
+
lastActive: number | null;
|
|
1113
|
+
daysSinceActive: number | null;
|
|
1114
|
+
subjectsPosted: number;
|
|
1115
|
+
commentsPosted: number;
|
|
1116
|
+
reflections: number;
|
|
1117
|
+
dailyTasksCompleted: number;
|
|
1118
|
+
wordlePlays: number;
|
|
1119
|
+
buildsTouched: number;
|
|
1120
|
+
aiStories: number;
|
|
1121
|
+
xpEvents: number;
|
|
1122
|
+
recommendationsGiven: number;
|
|
1123
|
+
rewardsGiven: number;
|
|
1124
|
+
rewardTwinklesGiven: number;
|
|
1125
|
+
interestScore: number;
|
|
1126
|
+
workScore: number;
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1091
1129
|
type InsightsBrief = Success<{
|
|
1092
1130
|
window: {
|
|
1093
1131
|
sinceTs: number;
|
|
@@ -1123,21 +1161,21 @@ type InsightsBrief = Success<{
|
|
|
1123
1161
|
isNewUser: boolean;
|
|
1124
1162
|
lastActive: number | null;
|
|
1125
1163
|
}>;
|
|
1126
|
-
teachers:
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
engagementScore: number;
|
|
1137
|
-
}>;
|
|
1164
|
+
teachers: {
|
|
1165
|
+
genuinelyInterested: TeacherInsight[];
|
|
1166
|
+
workOnly: TeacherInsight[];
|
|
1167
|
+
activeButSilent: TeacherInsight[];
|
|
1168
|
+
totals: {
|
|
1169
|
+
total: number;
|
|
1170
|
+
activeInWindow: number;
|
|
1171
|
+
interestedInWindow: number;
|
|
1172
|
+
};
|
|
1173
|
+
};
|
|
1138
1174
|
aiSpending:
|
|
1139
1175
|
| {
|
|
1140
1176
|
days: number;
|
|
1177
|
+
startDayIndex: number;
|
|
1178
|
+
endDayIndex: number;
|
|
1141
1179
|
summary: unknown;
|
|
1142
1180
|
topAccounts: unknown[];
|
|
1143
1181
|
topRiskGroups: unknown[];
|
|
@@ -1146,6 +1184,24 @@ type InsightsBrief = Success<{
|
|
|
1146
1184
|
}>;
|
|
1147
1185
|
```
|
|
1148
1186
|
|
|
1187
|
+
**Editing the bot's own comments.** `comment edit <commentId> --file
|
|
1188
|
+
<comment.md>` replaces the text of a comment the ACTING bot itself authored —
|
|
1189
|
+
for correcting a factual error, an unfulfillable claim, or outdated guidance
|
|
1190
|
+
in Zero/Ciel's own words. It is deliberately not a moderation verb: comments
|
|
1191
|
+
by the other bot, by any human, and hidden notification records are all
|
|
1192
|
+
rejected (`CLI_ADMIN_EDIT_NOT_OWN_COMMENT`,
|
|
1193
|
+
`CLI_ADMIN_EDIT_NOTIFICATION_COMMENT`). The replacement text follows the
|
|
1194
|
+
composed-comment rules (plain UTF-8, 10,000-character limit, truth about what
|
|
1195
|
+
the session actually did) and publishes through the website's canonical
|
|
1196
|
+
comment-edit pipeline — mentions are reprocessed (a newly added `@mikey`
|
|
1197
|
+
notifies him), and Earn-candidate projections resync. Submitting identical
|
|
1198
|
+
text returns `already_done`. Requires the `comment:post` scope of a
|
|
1199
|
+
comment-mode `post` run, and is audited as `comment.edit` with the previous
|
|
1200
|
+
content in `beforeState` and `data.edit.previousContent`. Edit sparingly:
|
|
1201
|
+
kids may have already read the original, so a comment that changed meaning
|
|
1202
|
+
(not just wording) usually deserves a follow-up reply instead of a silent
|
|
1203
|
+
rewrite.
|
|
1204
|
+
|
|
1149
1205
|
## Audit history
|
|
1150
1206
|
|
|
1151
1207
|
```bash
|
|
@@ -1216,6 +1272,9 @@ lumine admin comment reply comment:456 --json
|
|
|
1216
1272
|
|
|
1217
1273
|
lumine admin comment post --draft-id 77 \
|
|
1218
1274
|
--idempotency-key comment-123-post-v1 --json
|
|
1275
|
+
|
|
1276
|
+
# Correct the acting bot's OWN published comment.
|
|
1277
|
+
lumine admin comment edit 342752 --file corrected.md --json
|
|
1219
1278
|
```
|
|
1220
1279
|
|
|
1221
1280
|
**Compose in persona by default (Mikey's standing direction, 2026-08-10).**
|
|
@@ -1237,6 +1296,24 @@ from memory:
|
|
|
1237
1296
|
decisions are yours to make and record with `post skip` or in the run
|
|
1238
1297
|
report).
|
|
1239
1298
|
|
|
1299
|
+
**Lumine Build app posts are composed-only, and only after actually looking
|
|
1300
|
+
(Mikey's direction, 2026-08-10).** When a post is about a Build app — the
|
|
1301
|
+
author shares their app, announces an update, or asks for feedback on their
|
|
1302
|
+
build — never use the server-generated draft path: the API persona cannot
|
|
1303
|
+
open an app, so its drafts either stay generic or fabricate first-hand
|
|
1304
|
+
experience (a real Ciel draft claimed "I clicked over to check it out" on an
|
|
1305
|
+
app nobody had opened). The day's management agent comments instead, and
|
|
1306
|
+
looks first: pull the project with lumine-cli when it is open source (or
|
|
1307
|
+
yours to pull) and read the code, or open the app and actually try it; then
|
|
1308
|
+
compose a comment whose specifics come from what you genuinely saw — a
|
|
1309
|
+
mechanic you liked, a nice touch in their code, a concrete suggestion. Be
|
|
1310
|
+
truthful about what you did: "I read through your code" and "I played a few
|
|
1311
|
+
rounds" are different claims, and a comment must only make the one that
|
|
1312
|
+
happened. If you could not access the app at all, say nothing about having
|
|
1313
|
+
tried it — ask the author about it instead. This is the standing rule for
|
|
1314
|
+
every composed comment, applied to apps: never claim an experience the
|
|
1315
|
+
session did not actually have.
|
|
1316
|
+
|
|
1240
1317
|
A composed draft (`--file`, plain UTF-8 text, at most the website's 10,000
|
|
1241
1318
|
character comment limit) flows through the identical draft lifecycle —
|
|
1242
1319
|
reservation, idempotency, context-revision CAS, publish fencing, audit
|