@stage5/lumine 0.3.14 → 0.3.17
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/lib/admin-owner-trace.js +222 -0
- package/lib/admin.js +62 -13
- package/lib/build-review.js +2 -2
- package/lib/commands.js +17 -4
- package/lib/sponsor-duty.js +1201 -221
- package/lib/sponsor-preview.js +271 -0
- package/package.json +1 -1
- package/sdk/BUILD_SDK_INDEX.md +6 -6
- package/sdk/LUMINE_ADMIN.md +41 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// `lumine admin owner-trace`: the owner's client trace from Mikey's own
|
|
2
|
+
// devices (routes, Home feed card opens, scroll-anchor restores, content-page
|
|
3
|
+
// scroll positions, window errors), read when he reports a UI issue. Read-only;
|
|
4
|
+
// no daily run needed. The server keeps 30 days.
|
|
5
|
+
|
|
6
|
+
export const OWNER_TRACE_USAGE =
|
|
7
|
+
"Usage: lumine admin owner-trace [--since 2h|30m|3d|<iso>|<unix>] [--type route,feed-open,scroll-anchor,content-scroll,error,...] [--perf] [--path <substring>] [--limit 300] [--json].";
|
|
8
|
+
|
|
9
|
+
// --perf: only the performance events (tap-to-page timeline, slow API
|
|
10
|
+
// requests, main-thread stalls, page load).
|
|
11
|
+
export const OWNER_TRACE_PERF_TYPES = [
|
|
12
|
+
"nav-timing",
|
|
13
|
+
"slow-request",
|
|
14
|
+
"stall",
|
|
15
|
+
"page-load",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const MAX_LIMIT = 2000;
|
|
19
|
+
|
|
20
|
+
function validationError(message) {
|
|
21
|
+
const error = new Error(message);
|
|
22
|
+
error.code = "CLI_ADMIN_CLI_VALIDATION";
|
|
23
|
+
return error;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function normalizeSince(value) {
|
|
27
|
+
const raw = String(value || "").trim();
|
|
28
|
+
if (!raw) return "2h";
|
|
29
|
+
if (/^\d+\s*[smhd]$/i.test(raw) || /^\d{9,14}$/.test(raw)) {
|
|
30
|
+
return raw.replace(/\s+/g, "");
|
|
31
|
+
}
|
|
32
|
+
const parsed = Date.parse(raw);
|
|
33
|
+
if (!Number.isFinite(parsed)) {
|
|
34
|
+
throw validationError(
|
|
35
|
+
"--since must look like 30m, 2h, 3d, an ISO date or a unix time.",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return String(Math.floor(parsed / 1000));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function buildOwnerTraceOperation(options) {
|
|
42
|
+
const limitRaw = options.adminLimit;
|
|
43
|
+
let limit = 300;
|
|
44
|
+
if (limitRaw !== null && limitRaw !== undefined && limitRaw !== "") {
|
|
45
|
+
limit = Number(String(limitRaw).trim());
|
|
46
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
|
47
|
+
throw validationError(`--limit must be an integer 1-${MAX_LIMIT}.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const query = new URLSearchParams();
|
|
51
|
+
query.set("since", normalizeSince(options.adminSince));
|
|
52
|
+
query.set("limit", String(limit));
|
|
53
|
+
const types = String(options.adminType || "")
|
|
54
|
+
.split(",")
|
|
55
|
+
.map((type) => type.trim())
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
if (options.adminPerf) {
|
|
58
|
+
for (const type of OWNER_TRACE_PERF_TYPES) {
|
|
59
|
+
if (!types.includes(type)) types.push(type);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (types.length) query.set("type", types.join(","));
|
|
63
|
+
if (options.adminPath) query.set("path", String(options.adminPath));
|
|
64
|
+
return {
|
|
65
|
+
name: "owner-trace.read",
|
|
66
|
+
method: "GET",
|
|
67
|
+
path: `/cli/admin/owner-trace?${query.toString()}`,
|
|
68
|
+
body: undefined,
|
|
69
|
+
mutates: false,
|
|
70
|
+
requiresRun: false,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function pad(value, size) {
|
|
75
|
+
return String(value).padStart(size, "0");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function formatClock(ms) {
|
|
79
|
+
const date = new Date(ms);
|
|
80
|
+
return `${pad(date.getHours(), 2)}:${pad(date.getMinutes(), 2)}:${pad(date.getSeconds(), 2)}.${pad(date.getMilliseconds(), 3)}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatDay(ms) {
|
|
84
|
+
const date = new Date(ms);
|
|
85
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1, 2)}-${pad(date.getDate(), 2)}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function pairs(data, skip = []) {
|
|
89
|
+
return Object.entries(data || {})
|
|
90
|
+
.filter(([key, value]) => !skip.includes(key) && value !== undefined && value !== null && value !== "")
|
|
91
|
+
.map(([key, value]) => `${key}=${typeof value === "object" ? JSON.stringify(value) : value}`)
|
|
92
|
+
.join(" ");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function describeSession(data = {}, path = "") {
|
|
96
|
+
const parts = [
|
|
97
|
+
data.version ? `v${data.version}` : "",
|
|
98
|
+
data.vw && data.vh ? `${data.vw}x${data.vh}${data.dpr ? ` @${data.dpr}x` : ""}` : "",
|
|
99
|
+
data.vvh ? `visual ${data.vvh}h` : "",
|
|
100
|
+
data.platform || "",
|
|
101
|
+
data.mobile ? "mobile" : "",
|
|
102
|
+
data.standalone ? "standalone (PWA)" : "browser tab",
|
|
103
|
+
data.touch ? `touch ${data.touch}` : "",
|
|
104
|
+
].filter(Boolean);
|
|
105
|
+
if (path) parts.push(`at ${path}`);
|
|
106
|
+
return `${parts.join(" · ")}${data.ua ? `\n ua: ${data.ua}` : ""}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function describeOwnerTraceEvent(event) {
|
|
110
|
+
const data = event.data || {};
|
|
111
|
+
switch (event.type) {
|
|
112
|
+
case "route": {
|
|
113
|
+
const feed = data.feedNav
|
|
114
|
+
? ` · feedNav ${data.feedNav.ct}:${data.feedNav.id}${data.feedNav.action ? ` ${data.feedNav.action}` : ""}`
|
|
115
|
+
: "";
|
|
116
|
+
const intent = data.feedIntent ? ` · intent ${data.feedIntent}` : "";
|
|
117
|
+
const otherKeys = (data.stateKeys || []).filter(
|
|
118
|
+
(key) => key !== "homeFeedNavigation" && key !== "homeFeedActionIntent",
|
|
119
|
+
);
|
|
120
|
+
return `${data.nav || "?"}${data.from ? ` from ${data.from}` : ""}${feed}${intent}${otherKeys.length ? ` · state ${otherKeys.join(",")}` : ""}${data.hash ? ` · ${data.hash}` : ""}`;
|
|
121
|
+
}
|
|
122
|
+
case "feed-open":
|
|
123
|
+
return `${data.action || "open"} ${data.ct}:${data.id}${data.via ? ` via ${data.via}` : ""}${data.el ? ` (${data.el})` : ""}${data.to ? ` → ${data.to}` : ""}${data.pos ? ` · card #${data.pos}` : ""}${data.surface && data.surface !== "home" ? ` · ${data.surface}` : ""}`;
|
|
124
|
+
case "scroll-anchor":
|
|
125
|
+
return `${data.e || "?"} ${pairs(data, ["e"])}`.trim();
|
|
126
|
+
case "content-scroll":
|
|
127
|
+
return `${data.phase} ${data.ct}:${data.id} scrollTop=${data.st} height=${data.sh} viewport=${data.vh}`;
|
|
128
|
+
case "nav-timing":
|
|
129
|
+
return describeNavTiming(data);
|
|
130
|
+
case "slow-request": {
|
|
131
|
+
const extra = [
|
|
132
|
+
data.q ? `queued ${data.q}ms` : "",
|
|
133
|
+
data.tries ? `${data.tries} tries` : "",
|
|
134
|
+
].filter(Boolean);
|
|
135
|
+
return `${data.m || "?"} ${data.api || "?"} → ${data.status ?? "?"} in ${data.ms}ms${extra.length ? ` (${extra.join(", ")})` : ""}`;
|
|
136
|
+
}
|
|
137
|
+
case "stall":
|
|
138
|
+
return `main thread blocked ${data.ms}ms${data.n > 1 ? ` (${data.n} stalls, ${data.total}ms blocked over ${data.span}ms)` : ""}`;
|
|
139
|
+
case "page-load": {
|
|
140
|
+
const parts = [
|
|
141
|
+
data.type || "",
|
|
142
|
+
data.ttfb !== undefined ? `ttfb ${data.ttfb}` : "",
|
|
143
|
+
data.dcl !== undefined ? `domcontentloaded ${data.dcl}` : "",
|
|
144
|
+
data.load !== undefined ? `load ${data.load}` : "",
|
|
145
|
+
data.route !== undefined ? `first route ${data.route}` : "",
|
|
146
|
+
].filter(Boolean);
|
|
147
|
+
return `${parts.join(" · ")} ms${data.cached ? " (document from cache)" : ""}`;
|
|
148
|
+
}
|
|
149
|
+
case "error":
|
|
150
|
+
case "rejection":
|
|
151
|
+
return `${data.msg || "(no message)"}${data.src ? ` (${data.src}:${data.line}:${data.col})` : ""}${data.status ? ` HTTP ${data.status}` : ""}`;
|
|
152
|
+
default:
|
|
153
|
+
return pairs(data);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// "tap→page 4210ms · PUSH via nav · url +90 · gate +4180 (probe timeout,
|
|
158
|
+
// waited 4100ms) · commit +4210 · ready +4600": offsets are ms from the tap.
|
|
159
|
+
function describeNavTiming(data) {
|
|
160
|
+
const end = data.ready ?? data.commit;
|
|
161
|
+
const head =
|
|
162
|
+
end !== undefined
|
|
163
|
+
? `${data.via === "url" || data.via === "pop" ? "start" : "tap"}→${data.ready !== undefined ? "ready" : "page"} ${end}ms`
|
|
164
|
+
: `cut after ${data.total}ms`;
|
|
165
|
+
const steps = [
|
|
166
|
+
data.url !== undefined ? `url +${data.url}` : "",
|
|
167
|
+
data.gate !== undefined
|
|
168
|
+
? `gate +${data.gate}${data.probe ? ` (probe ${data.probe}${data.probeMs ? `, waited ${data.probeMs}ms` : ""})` : ""}`
|
|
169
|
+
: "",
|
|
170
|
+
data.commit !== undefined ? `commit +${data.commit}` : "",
|
|
171
|
+
data.ready !== undefined ? `ready +${data.ready}` : "",
|
|
172
|
+
].filter(Boolean);
|
|
173
|
+
const tail = [
|
|
174
|
+
data.result ? `gate ${data.result}` : "",
|
|
175
|
+
data.cut ? `cut by ${data.cut}` : "",
|
|
176
|
+
].filter(Boolean);
|
|
177
|
+
return [head, `${data.nav || "?"} via ${data.via || "?"}`, ...steps, ...tail].join(" · ");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function formatOwnerTrace(data) {
|
|
181
|
+
const events = Array.isArray(data?.events) ? data.events : [];
|
|
182
|
+
const lines = [];
|
|
183
|
+
const sessions = new Set(events.map((event) => event.sessionId));
|
|
184
|
+
const since = Number(data?.since || 0);
|
|
185
|
+
lines.push(
|
|
186
|
+
`Owner trace since ${since ? `${formatDay(since * 1000)} ${formatClock(since * 1000).slice(0, 5)}` : "?"} · ${events.length} event(s) · ${sessions.size} session(s)${data?.truncated ? " · older events cut off (raise --limit or narrow --since)" : ""}`,
|
|
187
|
+
);
|
|
188
|
+
if (!events.length) {
|
|
189
|
+
lines.push("Nothing recorded in this window.");
|
|
190
|
+
return lines;
|
|
191
|
+
}
|
|
192
|
+
let day = "";
|
|
193
|
+
let session = "";
|
|
194
|
+
for (const event of events) {
|
|
195
|
+
const ms = Number(event.clientTime) || Number(event.serverTime) * 1000;
|
|
196
|
+
const eventDay = formatDay(ms);
|
|
197
|
+
if (eventDay !== day) {
|
|
198
|
+
day = eventDay;
|
|
199
|
+
lines.push(`── ${day}`);
|
|
200
|
+
}
|
|
201
|
+
if (event.sessionId !== session) {
|
|
202
|
+
session = event.sessionId;
|
|
203
|
+
if (event.type !== "session") {
|
|
204
|
+
lines.push(` · session ${String(session).slice(0, 8)} (continued)`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (event.type === "session") {
|
|
208
|
+
lines.push(
|
|
209
|
+
` ${formatClock(ms)} ── session ${String(session).slice(0, 8)} · ${describeSession(event.data, event.path)}`,
|
|
210
|
+
);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
lines.push(
|
|
214
|
+
` ${formatClock(ms)} ${event.type.padEnd(14)} ${event.path || "-"} ${describeOwnerTraceEvent(event)}`.trimEnd(),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
return lines;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function printOwnerTrace(data) {
|
|
221
|
+
for (const line of formatOwnerTrace(data)) console.log(line);
|
|
222
|
+
}
|
package/lib/admin.js
CHANGED
|
@@ -33,6 +33,10 @@ import {
|
|
|
33
33
|
runManagedBuildReview,
|
|
34
34
|
} from "./build-review.js";
|
|
35
35
|
import { runAdminRuntimeLogWorkflow } from "./admin-runtime-logs.js";
|
|
36
|
+
import {
|
|
37
|
+
buildOwnerTraceOperation,
|
|
38
|
+
printOwnerTrace,
|
|
39
|
+
} from "./admin-owner-trace.js";
|
|
36
40
|
import {
|
|
37
41
|
dailyRunGather,
|
|
38
42
|
dailyRunProgress,
|
|
@@ -285,7 +289,7 @@ function buildApprovalOperation({ action, target, options }) {
|
|
|
285
289
|
// Code agent passes wrote (no model is called), then review what they could
|
|
286
290
|
// not settle (Mikey 2026-10-06: bulk AI work runs on Claude Code agents).
|
|
287
291
|
const ENGLISH_USAGE =
|
|
288
|
-
"Use: lumine admin english import <verdicts.json> --pass a|b | adjudicate <verdicts.json> | add-questions <questions.json> | relevel <moves.json> | summary | review [--limit <n>] | accept|retire <questionId> [--reason <text>] | retag <questionId> --skill <code> [--reason <text>]";
|
|
292
|
+
"Use: lumine admin english import <verdicts.json> --pass a|b | adjudicate <verdicts.json> | add-questions <questions.json> | relevel <moves.json> | mastery <userId> --reason <why> | backfill-distractors | summary | review [--limit <n>] | accept|retire <questionId> [--reason <text>] | retag <questionId> --skill <code> [--reason <text>]";
|
|
289
293
|
const ENGLISH_IMPORT_MAX = 500;
|
|
290
294
|
|
|
291
295
|
function buildEnglishOperation({ action, target, options }) {
|
|
@@ -320,6 +324,20 @@ function buildEnglishOperation({ action, target, options }) {
|
|
|
320
324
|
if (action === "summary") {
|
|
321
325
|
return readOperation("english.summary", "/cli/admin/english/tag-summary", { requiresRun: false });
|
|
322
326
|
}
|
|
327
|
+
// One learner's per-skill mastery and the sentences a report may use.
|
|
328
|
+
// Private learner data: a reason is required and every read is audited.
|
|
329
|
+
if (action === "mastery") {
|
|
330
|
+
const userId = String(target || "");
|
|
331
|
+
const reason = String(options.adminReason || "").trim();
|
|
332
|
+
if (!/^\d+$/.test(userId) || reason.length < 8 || reason.length > 500) throw cliValidationError(ENGLISH_USAGE);
|
|
333
|
+
// POST: the reason travels in the body, never in a URL that access logs keep.
|
|
334
|
+
// A read, not a write: writes save their full response as a receipt file,
|
|
335
|
+
// and a child's learning data must never be left on disk.
|
|
336
|
+
return { ...bodyReadOperation("english.mastery", "POST", "/cli/admin/english/mastery", { userId: Number(userId), reason }), requiresRun: false, timeoutMs: 60000 };
|
|
337
|
+
}
|
|
338
|
+
if (action === "backfill-distractors") {
|
|
339
|
+
return writeOperation("english.backfill_distractors", "POST", "/cli/admin/english/backfill-distractors", {}, { requiresRun: false, timeoutMs: 300000 });
|
|
340
|
+
}
|
|
323
341
|
if (action === "review" && !target) {
|
|
324
342
|
return readOperation("english.review", withQuery("/cli/admin/english/review", { limit: limitOf(100, 20) }), { requiresRun: false });
|
|
325
343
|
}
|
|
@@ -2005,6 +2023,11 @@ export function parseAdminOperation(options) {
|
|
|
2005
2023
|
const [namespace = "", action = "", target = "", extra = ""] =
|
|
2006
2024
|
options.positional;
|
|
2007
2025
|
|
|
2026
|
+
// The owner's client trace (admin-owner-trace.js); read-only, no run.
|
|
2027
|
+
if (namespace === "owner-trace") {
|
|
2028
|
+
return buildOwnerTraceOperation(options);
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2008
2031
|
if (namespace === "correction" || namespace === "corrections") {
|
|
2009
2032
|
if (!action || action === "status") {
|
|
2010
2033
|
return readOperation(
|
|
@@ -2074,7 +2097,11 @@ export function parseAdminOperation(options) {
|
|
|
2074
2097
|
`An identity-inspection reason must be at most ${MAX_IDENTITY_INSPECTION_REASON_LENGTH} characters.`,
|
|
2075
2098
|
);
|
|
2076
2099
|
}
|
|
2077
|
-
|
|
2100
|
+
// A read: writes keep their full response as a receipt file under
|
|
2101
|
+
// ./work/lumine-admin, and private identity evidence must never be left
|
|
2102
|
+
// on disk by default. The server still audits who looked, when and why
|
|
2103
|
+
// (one row per call). `--output <file>` saves a 0600 copy on purpose.
|
|
2104
|
+
return bodyReadOperation(
|
|
2078
2105
|
"identity.inspect",
|
|
2079
2106
|
"POST",
|
|
2080
2107
|
"/cli/admin/identity/inspect",
|
|
@@ -2100,17 +2127,20 @@ export function parseAdminOperation(options) {
|
|
|
2100
2127
|
`A network-evidence reason must be at most ${MAX_IDENTITY_INSPECTION_REASON_LENGTH} characters.`,
|
|
2101
2128
|
);
|
|
2102
2129
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2130
|
+
// A read for the same reason as identity inspect: no receipt on disk.
|
|
2131
|
+
return {
|
|
2132
|
+
...bodyReadOperation(
|
|
2133
|
+
"identity.network",
|
|
2134
|
+
"POST",
|
|
2135
|
+
"/cli/admin/identity/network",
|
|
2136
|
+
{
|
|
2137
|
+
target: networkTarget,
|
|
2138
|
+
reason,
|
|
2139
|
+
includePrivateEvidence: options.adminIncludePrivateEvidence === true,
|
|
2140
|
+
},
|
|
2141
|
+
),
|
|
2142
|
+
requiresRun: false,
|
|
2143
|
+
};
|
|
2114
2144
|
}
|
|
2115
2145
|
|
|
2116
2146
|
if (namespace === "economy" && action === "trace") {
|
|
@@ -5878,6 +5908,21 @@ function printEnglishResult({ operation, data }) {
|
|
|
5878
5908
|
console.log(`Pass ${data.pass}: ${data.stored} stored of ${data.received} (${data.invalid} invalid, ${data.unknownQuestion} unknown questions) · ${data.decided} decided: ${data.auto} accepted automatically, ${data.review} for review (${Object.entries(data.reasons || {}).map(([k, v]) => `${k} ${v}`).join(", ") || "-"})`);
|
|
5879
5909
|
return;
|
|
5880
5910
|
}
|
|
5911
|
+
if (operation.name === "english.mastery") {
|
|
5912
|
+
console.log(`User #${data.userId} · last ${data.windowDays} days · ${data.responses} honest answers · ${data.skillsSeen} skills seen (mastery = ${Math.round(data.target * 100)}% right on the first try)`);
|
|
5913
|
+
const label = { fact: "facts only", focus: "NEXT FOCUS", gap: "CLEAR GAP", strength: "strength", "clear-strength": "CLEAR STRENGTH" };
|
|
5914
|
+
if (!data.reportable?.length) console.log("Nothing reportable yet: no skill has enough evidence.");
|
|
5915
|
+
for (const r of data.reportable || []) {
|
|
5916
|
+
console.log(` [${label[r.kind] || r.kind}] ${r.nameEn} · ${r.correct}/${r.responses} first try · ${r.items} questions · ${r.days} days · P(below) ${r.pBelow.toFixed(2)}`);
|
|
5917
|
+
console.log(` report may say: ${r.wordingEn}`);
|
|
5918
|
+
}
|
|
5919
|
+
if (data.practiceOnly?.length) console.log(` practice only (not enough evidence yet): ${data.practiceOnly.map((s) => `${s.nameEn} ${s.responses}`).join(" · ")}`);
|
|
5920
|
+
return;
|
|
5921
|
+
}
|
|
5922
|
+
if (operation.name === "english.backfill_distractors") {
|
|
5923
|
+
console.log(`Wrong-choice labels restored on ${data.filled} of ${data.candidates} agent-written questions.`);
|
|
5924
|
+
return;
|
|
5925
|
+
}
|
|
5881
5926
|
if (operation.name === "english.adjudicate") {
|
|
5882
5927
|
console.log(`Adjudicated ${data.received}: ${data.keep} kept · ${data.fix} fixed (new versions) · ${data.retire} retired · ${data.skipped?.length || 0} skipped${data.skipped?.length ? ` (${data.skipped.slice(0, 5).map((s) => `${s.ref}: ${s.why}`).join("; ")})` : ""}`);
|
|
5883
5928
|
return;
|
|
@@ -6061,6 +6106,10 @@ function printAdminResult({ operation, result }) {
|
|
|
6061
6106
|
printRewardBankReport(data);
|
|
6062
6107
|
return;
|
|
6063
6108
|
}
|
|
6109
|
+
if (operation.name === "owner-trace.read") {
|
|
6110
|
+
printOwnerTrace(data);
|
|
6111
|
+
return;
|
|
6112
|
+
}
|
|
6064
6113
|
if (operation.name.startsWith("reward-bank.")) {
|
|
6065
6114
|
if (operation.name === "reward-bank.seed") {
|
|
6066
6115
|
console.log(
|
package/lib/build-review.js
CHANGED
|
@@ -536,7 +536,7 @@ export async function runInteractionSteps({
|
|
|
536
536
|
};
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
-
function findChromeExecutable(explicitPath = "") {
|
|
539
|
+
export function findChromeExecutable(explicitPath = "") {
|
|
540
540
|
const candidates = [
|
|
541
541
|
explicitPath,
|
|
542
542
|
process.env.CHROME_PATH,
|
|
@@ -589,7 +589,7 @@ function delay(milliseconds) {
|
|
|
589
589
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
-
function createCdpPipe(child) {
|
|
592
|
+
export function createCdpPipe(child) {
|
|
593
593
|
const commandPipe = child.stdio[3];
|
|
594
594
|
const eventPipe = child.stdio[4];
|
|
595
595
|
if (!commandPipe || !eventPipe) {
|
package/lib/commands.js
CHANGED
|
@@ -2371,6 +2371,8 @@ export function parseArgs(args) {
|
|
|
2371
2371
|
"json",
|
|
2372
2372
|
"keepAssets",
|
|
2373
2373
|
"noBrowser",
|
|
2374
|
+
"noSave",
|
|
2375
|
+
"poolEvents",
|
|
2374
2376
|
"includeComments",
|
|
2375
2377
|
"anyoneCanReward",
|
|
2376
2378
|
"full",
|
|
@@ -2379,6 +2381,7 @@ export function parseArgs(args) {
|
|
|
2379
2381
|
"all",
|
|
2380
2382
|
"resume",
|
|
2381
2383
|
"sinceRun",
|
|
2384
|
+
"perf",
|
|
2382
2385
|
"includeLegacy",
|
|
2383
2386
|
"includePrivateEvidence",
|
|
2384
2387
|
"reviewed",
|
|
@@ -2493,6 +2496,9 @@ export function parseArgs(args) {
|
|
|
2493
2496
|
adminPostedAfter: raw.postedAfter ? String(raw.postedAfter) : "",
|
|
2494
2497
|
adminApprove: raw.approve ? String(raw.approve) : "",
|
|
2495
2498
|
adminSinceRun: Boolean(raw.sinceRun),
|
|
2499
|
+
adminSince: raw.since ? String(raw.since) : "",
|
|
2500
|
+
adminPath: raw.path ? String(raw.path) : "",
|
|
2501
|
+
adminPerf: Boolean(raw.perf),
|
|
2496
2502
|
adminIncludeLegacy: Boolean(raw.includeLegacy),
|
|
2497
2503
|
adminIncludePrivateEvidence: parseBoolean(
|
|
2498
2504
|
raw.includePrivateEvidence,
|
|
@@ -2546,6 +2552,10 @@ export function parseArgs(args) {
|
|
|
2546
2552
|
sponsorOutcome: raw.outcome ? String(raw.outcome) : "",
|
|
2547
2553
|
sponsorFailureReason: raw.reason ? String(raw.reason) : "",
|
|
2548
2554
|
sponsorNoLeaseKeeper: Boolean(raw.noLeaseKeeper),
|
|
2555
|
+
sponsorPoolEvents: Boolean(raw.poolEvents),
|
|
2556
|
+
sponsorNotifyCommand: raw.notify ? String(raw.notify) : "",
|
|
2557
|
+
sponsorLoopMinutes: raw.minutes ?? "",
|
|
2558
|
+
sponsorNoSave: Boolean(raw.noSave),
|
|
2549
2559
|
sponsorHoldEverySeconds: raw.holdEverySeconds,
|
|
2550
2560
|
sponsorUpdateFile: raw.file ? String(raw.file) : "",
|
|
2551
2561
|
sponsorUpdatePhase: raw.phase ? String(raw.phase) : "",
|
|
@@ -2920,7 +2930,8 @@ export function printHelp() {
|
|
|
2920
2930
|
lumine sponsor agreement|apply|status|withdraw
|
|
2921
2931
|
lumine sponsor capacity [--concurrency <n>] [--helpers <n>] [--daily-limit <n>] [--weekly-limit <n>]
|
|
2922
2932
|
lumine sponsor duty start [--provider <codex|claude-code>] --model <name> --effort <level> [--service-tier <tier>]
|
|
2923
|
-
lumine sponsor duty watch [--wait-ms <ms>] [--json]
|
|
2933
|
+
lumine sponsor duty watch [--wait-ms <ms>] [--pool-events] [--json]
|
|
2934
|
+
lumine sponsor duty watch-loop [--minutes <n>] [--pool-events] [--notify <command>] [--json]
|
|
2924
2935
|
lumine sponsor duty pause|resume|stop
|
|
2925
2936
|
lumine sponsor job status|pulse|begin <job-id>
|
|
2926
2937
|
lumine sponsor job update <job-id> --file <path> [--phase <name>]
|
|
@@ -2933,6 +2944,7 @@ export function printHelp() {
|
|
|
2933
2944
|
lumine sponsor job rename <job-id> <title>
|
|
2934
2945
|
lumine sponsor job describe <job-id> <text>
|
|
2935
2946
|
lumine sponsor job update <job-id> --file <path> --kind question
|
|
2947
|
+
lumine sponsor job preview <job-id> [--no-save] [--no-browser] [--wait-ms <ms>]
|
|
2936
2948
|
lumine sponsor job complete <job-id> --summary <text>
|
|
2937
2949
|
lumine sponsor job fail <job-id> --reason <text>
|
|
2938
2950
|
lumine sponsor jobs
|
|
@@ -2991,8 +3003,8 @@ export function printHelp() {
|
|
|
2991
3003
|
lumine thumbnail generate ["<prompt>"] --model <gpt-image-2.5-flare|gpt-image-2.5-sunburst|gpt-image-2|nano-banana>
|
|
2992
3004
|
lumine doctor runtime-assets
|
|
2993
3005
|
lumine admin identity list|status|use <zero|ciel|auto> [--json]
|
|
2994
|
-
lumine admin identity inspect <user-id|username> --reason <management-reason> [--include-private-evidence] [--json]
|
|
2995
|
-
lumine admin identity network <user-id|username> --reason <management-reason> [--include-private-evidence] [--json] (addresses this account used; who shares the small ones)
|
|
3006
|
+
lumine admin identity inspect <user-id|username> --reason <management-reason> [--include-private-evidence] [--output <file>] [--json] (nothing kept on disk unless --output; that copy is 0600)
|
|
3007
|
+
lumine admin identity network <user-id|username> --reason <management-reason> [--include-private-evidence] [--output <file>] [--json] (addresses this account used; who shares the small ones)
|
|
2996
3008
|
lumine admin economy trace <user-id|username> --reason <management-reason> [--days <1..30>] [--json]
|
|
2997
3009
|
lumine admin rescue wordle-audit --reason <management-reason> [--days <1..30>] [--json]
|
|
2998
3010
|
lumine admin ai-bucket create --label <name> --note <text> [--json]
|
|
@@ -3019,7 +3031,7 @@ export function printHelp() {
|
|
|
3019
3031
|
lumine admin review list [--type rewards|project|storage|cardcraft] [--status queue|pending|approved|rejected|all] [--cursor <c>] | show <type:id> [--dir <path>] | approve <type:id> [--config <rules.json>] [--size <500MB|1GB|2GB|n MB>] [--reason <text>] | reject|revoke <type:id> [--reason <text>] | propose rewards:<id> --dir <edited-snapshot> --config <rules.json>
|
|
3020
3032
|
lumine admin approvals list [--status pending|approved|rejected|all] [--type dob|mentor|meetup] | show <id> | approve <id> [--reason <text>] | reject <id> [--reason <text>] | supermod <id> [--reason <text>] (management approvals with evidence: birthdates, teacher signups, meetups)
|
|
3021
3033
|
lumine admin teachers audit [--limit <1-100>] | revoke <userId> --reason <text> (approved teachers, riskiest first; revoke removes the status cleanly)
|
|
3022
|
-
lumine admin english import <verdicts.json> --pass a|b | adjudicate <file> | add-questions <file> | relevel <file> | summary | review [--limit <n>] | accept|retire <questionId> | retag <questionId> --skill <code> (English skill tags and bank upkeep for Grammarbles; content written by Claude Code agents)
|
|
3034
|
+
lumine admin english import <verdicts.json> --pass a|b | adjudicate <file> | add-questions <file> | relevel <file> | mastery <userId> --reason <why> | backfill-distractors | summary | review [--limit <n>] | accept|retire <questionId> | retag <questionId> --skill <code> (English skill tags and bank upkeep for Grammarbles; content written by Claude Code agents)
|
|
3023
3035
|
lumine admin reward-review list [--status pending|approved|all] [--cursor <id>] [--json]
|
|
3024
3036
|
lumine admin cardcraft-review list [--status pending|approved|all] [--cursor <id>] | show <id> | approve|reject|revoke <id> [--reason <text>]
|
|
3025
3037
|
lumine admin storage list [--status pending|approved|rejected|all] [--cursor <id>] | show <user-id-or-username> | approve <request-id> [--size <500MB|1GB|2GB|n MB>] [--reason <text>] | reject <request-id> [--reason <text>] | grant <user-id-or-username> --size <500MB|1GB|2GB|n MB> [--reason <text>]
|
|
@@ -3071,6 +3083,7 @@ export function printHelp() {
|
|
|
3071
3083
|
lumine admin reward-bank coverage <buildId> [--max-step 3] [--json]
|
|
3072
3084
|
lumine admin reward-bank seed <buildId> [--max-step 3] [--identity zero|ciel|auto] [--json] (writes ONE missing seed question, lowest step first, billed to Zero/Ciel's AI Energy; inside a daily run or on its own, owner only)
|
|
3073
3085
|
lumine admin reward-bank report <buildId> [--days 1] [--json] (question-bank telemetry: generation outcomes, learners, payouts, ladder, questions to review)
|
|
3086
|
+
lumine admin owner-trace [--since 2h|30m|3d|<iso>] [--type route,feed-open,scroll-anchor,content-scroll,error,rejection,session,nav-timing,slow-request,stall,page-load] [--perf] [--path <substring>] [--limit 300] [--json] (what the website did on Mikey's own devices: routes with PUSH/POP/REPLACE, Home feed card opens, scroll-anchor restores, content scroll positions, window errors, and performance: tap-to-page timing, API requests over 1 s, main-thread stalls over 200 ms, page load; --perf shows only those; kept 30 days)
|
|
3074
3087
|
lumine admin comment draft <target> [--type subject|comment|build|aiStory|dailyReflection] [--file <comment.md>] [--review-receipt <review.json>|--reviewed-version <id> --reviewed-via runtime|code] [--review-context <context.json>] [--identity zero|ciel|auto] [--json]
|
|
3075
3088
|
lumine admin comment reply comment:<id> [--file <reply.md>] [--reviewed-version <id> --reviewed-via runtime|code] [--review-context <context.json>] [--identity zero|ciel|auto] [--json]
|
|
3076
3089
|
lumine admin comment post --draft-id <id> [--json]
|