recess-cli 2.6.1 → 2.8.0
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 +9 -1
- package/dist/api.js +13 -8
- package/dist/args.js +0 -1
- package/dist/cli.js +363 -46
- package/dist/command-schema.js +197 -34
- package/dist/commands/apps.js +282 -23
- package/dist/commands/mastery.js +214 -0
- package/dist/commands/onboarding.js +1 -7
- package/dist/commands/school.js +1 -1
- package/dist/commands/village-events.js +26 -3
- package/dist/engine.js +6 -0
- package/dist/errors.js +13 -4
- package/dist/help.js +23 -6
- package/dist/upload-names.js +19 -0
- package/package.json +8 -1
- package/skill/recess-cli/SKILL.md +31 -1
- package/skill/recess-cli/agents/version.json +2 -2
|
@@ -2,6 +2,7 @@ import { unwrap } from "../api.js";
|
|
|
2
2
|
import { flagString, hasFlag } from "../args.js";
|
|
3
3
|
import { apiError, CliError } from "../errors.js";
|
|
4
4
|
const NONE = "—";
|
|
5
|
+
const WEEKDAYS = ["MO", "TU", "WE", "TH", "FR", "SA", "SU"];
|
|
5
6
|
async function fetchTemplatesAndRooms(ctx) {
|
|
6
7
|
const [templatesResponse, rooms] = await Promise.all([
|
|
7
8
|
ctx.api.client.GET("/recess/event-templates-editable/"),
|
|
@@ -17,6 +18,17 @@ function schedule(template) {
|
|
|
17
18
|
return template.singleEventDate.slice(0, 10);
|
|
18
19
|
return template.rrule || NONE;
|
|
19
20
|
}
|
|
21
|
+
function templateDays(template) {
|
|
22
|
+
if (template.singleEventDate)
|
|
23
|
+
return null;
|
|
24
|
+
const match = /BYDAY=([A-Z,]+)/.exec(template.rrule);
|
|
25
|
+
if (!match)
|
|
26
|
+
return null;
|
|
27
|
+
const days = match[1]
|
|
28
|
+
.split(",")
|
|
29
|
+
.filter((day) => WEEKDAYS.includes(day));
|
|
30
|
+
return days.length > 0 ? days : null;
|
|
31
|
+
}
|
|
20
32
|
function renderColumns(rows) {
|
|
21
33
|
const widths = rows[0].map((_, column) => Math.max(...rows.map((row) => row[column].length)));
|
|
22
34
|
return rows
|
|
@@ -45,7 +57,7 @@ async function listEvents(ctx) {
|
|
|
45
57
|
};
|
|
46
58
|
});
|
|
47
59
|
const unlinkedRooms = rooms
|
|
48
|
-
.filter((room) =>
|
|
60
|
+
.filter((room) => room.bookings.length === 0)
|
|
49
61
|
.map((room) => ({ id: room.id, name: room.name }));
|
|
50
62
|
if (hasFlag(ctx.parsed, "json")) {
|
|
51
63
|
return { templates: templateRows, unlinkedRooms };
|
|
@@ -97,14 +109,22 @@ async function linkEvent(ctx) {
|
|
|
97
109
|
throw new CliError("not_found", `No editable event template found for ${templateId}. Run \`recess village events\` to list them.`);
|
|
98
110
|
}
|
|
99
111
|
let room;
|
|
112
|
+
let informationalBookings = [];
|
|
100
113
|
if (roomId) {
|
|
101
114
|
room = rooms.find((row) => row.id === roomId);
|
|
102
115
|
if (!room) {
|
|
103
116
|
throw new CliError("not_found", `No Village Town Center zone found for ${roomId}. Run \`recess village events\` to list linkable zones.`);
|
|
104
117
|
}
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
const otherBookings = room.bookings.filter((booking) => booking.templateId !== templateId);
|
|
119
|
+
const days = templateDays(template);
|
|
120
|
+
const conflict = days
|
|
121
|
+
? otherBookings.find((booking) => booking.days.some((day) => days.includes(day)))
|
|
122
|
+
: undefined;
|
|
123
|
+
if (conflict) {
|
|
124
|
+
throw new CliError("invalid_arguments", `Zone ${room.id} (${room.name}) is already linked to template ${conflict.templateId} (${conflict.name}). Unlink that template first.`);
|
|
107
125
|
}
|
|
126
|
+
if (!days)
|
|
127
|
+
informationalBookings = otherBookings;
|
|
108
128
|
}
|
|
109
129
|
const previousRoom = template.villageRoomId
|
|
110
130
|
? rooms.find((row) => row.id === template.villageRoomId)
|
|
@@ -123,6 +143,9 @@ async function linkEvent(ctx) {
|
|
|
123
143
|
currentVillageRoomId: template.villageRoomId,
|
|
124
144
|
},
|
|
125
145
|
request: body,
|
|
146
|
+
...(informationalBookings.length > 0 && {
|
|
147
|
+
details: { bookings: informationalBookings },
|
|
148
|
+
}),
|
|
126
149
|
}, async () => {
|
|
127
150
|
const result = await api.client.PATCH("/recess/event-templates/{id}/", {
|
|
128
151
|
params: { path: { id: template.id } },
|
package/dist/engine.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { executeRecessCommand, } from "./cli.js";
|
|
2
|
+
export { buildCommandSchema, findCommandSchema, RECESS_MCP_SCOPES, } from "./command-schema.js";
|
|
3
|
+
export { RecessAdminApi } from "./api.js";
|
|
4
|
+
export { CliError } from "./errors.js";
|
|
5
|
+
export { APP_BUNDLE_EDITABLE_FILES } from "./commands/apps.js";
|
|
6
|
+
//# sourceMappingURL=engine.js.map
|
package/dist/errors.js
CHANGED
|
@@ -10,9 +10,18 @@ export class CliError extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
export function apiError(status, body) {
|
|
13
|
-
const message = typeof body === "object" && body && "
|
|
14
|
-
? String(body.
|
|
15
|
-
:
|
|
16
|
-
|
|
13
|
+
const message = typeof body === "object" && body && "error" in body
|
|
14
|
+
? String(body.error)
|
|
15
|
+
: typeof body === "object" && body && "message" in body
|
|
16
|
+
? String(body.message)
|
|
17
|
+
: `Recess API request failed with status ${status}`;
|
|
18
|
+
const code = status === 404
|
|
19
|
+
? "not_found"
|
|
20
|
+
: status === 409
|
|
21
|
+
? "conflict"
|
|
22
|
+
: status === 403
|
|
23
|
+
? "forbidden"
|
|
24
|
+
: "api_error";
|
|
25
|
+
return new CliError(code, message, 1, { status, body });
|
|
17
26
|
}
|
|
18
27
|
//# sourceMappingURL=errors.js.map
|
package/dist/help.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
export const HELP = `recess — safe Recess administration and family AI tools
|
|
2
2
|
|
|
3
3
|
Usage:
|
|
4
|
+
recess [--json] mastery graphs [--domain <slug>]
|
|
5
|
+
recess [--json] mastery get <graph-id> [--kind seed|draft|release]
|
|
6
|
+
recess [--json] mastery edit <graph-id> [--kind seed|draft|release]
|
|
7
|
+
[--file <graph.json> | --instruction TEXT] [--node <node-id>] [--confirm]
|
|
8
|
+
recess [--json] mastery publish <draft-id> --expected-active-release <release-id-or-none> [--confirm]
|
|
9
|
+
recess [--json] mastery links --graph <release-id> [--node <node-id>] [--cursor <link-id>]
|
|
10
|
+
recess [--json] mastery link <content-id> --type applet|goal --graph <release-id>
|
|
11
|
+
[--node <node-id>] [--confirm]
|
|
12
|
+
recess [--json] mastery unlink <link-id> [--confirm]
|
|
4
13
|
recess [--json] --version
|
|
14
|
+
recess [--json] help [noun] [verb]
|
|
5
15
|
recess [--json] agent-context
|
|
6
16
|
recess [--json] setup [--skill-only]
|
|
7
17
|
recess [--json] doctor
|
|
@@ -39,10 +49,11 @@ Usage:
|
|
|
39
49
|
--first-name TEXT [--last-name TEXT] [--no-invite] [--confirm]
|
|
40
50
|
recess [--json] students upload-map-scores --student <kid-id>
|
|
41
51
|
--file </path/to/map-report.pdf> [--confirm]
|
|
42
|
-
recess [--json] students list
|
|
52
|
+
recess [--json] students list [--scope mine|family]
|
|
43
53
|
recess [--json] students today --student <kid-id> [--date YYYY-MM-DD]
|
|
44
54
|
recess [--json] students schedule --student <kid-id> [--days 14]
|
|
45
|
-
recess [--json] students todos --student <
|
|
55
|
+
recess [--json] students todos --student <id-or-name> [--date <today-or-YYYY-MM-DD>]
|
|
56
|
+
[--limit 30] [--cursor <todo-id>] [--analyzed]
|
|
46
57
|
recess [--json] students analysis --todo <todo-id>
|
|
47
58
|
recess [--json] students xp-history --student <kid-id>
|
|
48
59
|
[--range week|month|quarter|year]
|
|
@@ -55,6 +66,7 @@ Usage:
|
|
|
55
66
|
--method refund|credit|tokens [--full | --amount-cents N]
|
|
56
67
|
[--who-pays guide|recess] [--reason TEXT] [--confirm]
|
|
57
68
|
recess [--json] apps init <dir> [--for-todo <todo-id>] [--force]
|
|
69
|
+
recess [--json] apps scaffold [--for-todo <todo-id>]
|
|
58
70
|
recess [--json] apps preview [dir] [--port N]
|
|
59
71
|
recess [--json] apps primitives list
|
|
60
72
|
recess [--json] apps primitives publish <file.js> --name <kebab-name> [--description TEXT]
|
|
@@ -301,12 +313,12 @@ Usage:
|
|
|
301
313
|
--status ACTIVE|INACTIVE|COMING_SOON [--confirm]
|
|
302
314
|
recess [--json] content-library search <query> [--limit 8]
|
|
303
315
|
recess [--json] content-library status <gem-id-or-url>
|
|
304
|
-
recess [--json] content-library set-stage <gem-id-or-url
|
|
316
|
+
recess [--json] content-library set-stage [<gem-id-or-url>...] [--file <path>]
|
|
305
317
|
--stage review|polishing|live|archived [--wait] [--timeout 900] [--confirm]
|
|
306
|
-
recess [--json] content-library submit <url
|
|
318
|
+
recess [--json] content-library submit [<url>...] [--file <path>]
|
|
307
319
|
[--stage review|polish] [--title TEXT] [--summary TEXT]
|
|
308
|
-
[--lane web-toys|mechanics|explorables|data-stories|sims|maps-scale|sound-art|puzzles|wonder|idea-games]
|
|
309
|
-
[--wait] [--timeout 900] [--confirm]
|
|
320
|
+
[--lane web-toys|mechanics|explorables|data-stories|sims|maps-scale|sound-art|puzzles|wonder|idea-games|makers|drills]
|
|
321
|
+
[--allow-possible-duplicate] [--wait] [--timeout 900] [--confirm]
|
|
310
322
|
recess [--json] skills guardian list [--query TEXT] [--category TEXT]
|
|
311
323
|
recess [--json] skills guardian get <skill-name>
|
|
312
324
|
[--reference NAME | --all-references] [--refresh]
|
|
@@ -353,6 +365,8 @@ Usage:
|
|
|
353
365
|
--delta TEXT [--confirm --approval-token TOKEN]
|
|
354
366
|
recess [--json] goals delete <goal-id> --student <kid-id>
|
|
355
367
|
[--confirm --approval-token TOKEN]
|
|
368
|
+
recess [--json] goals restore <goal-id> --student <kid-id>
|
|
369
|
+
[--confirm --approval-token TOKEN]
|
|
356
370
|
recess [--json] goals complete <goal-id> [--confirm]
|
|
357
371
|
recess [--json] goals undo-completion <goal-id> [--confirm]
|
|
358
372
|
recess [--json] goals archive <goal-id> --student <kid-id>
|
|
@@ -371,6 +385,9 @@ Usage:
|
|
|
371
385
|
[--confirm --approval-token TOKEN]
|
|
372
386
|
recess [--json] todos delete <todo-id>
|
|
373
387
|
[--confirm --approval-token TOKEN]
|
|
388
|
+
recess [--json] todos restore <todo-id>
|
|
389
|
+
[--confirm --approval-token TOKEN]
|
|
390
|
+
recess [--json] todos generate-learning-analysis <todo-id> [--confirm]
|
|
374
391
|
recess [--json] todos generate-applet <todo-id> --student <kid-id>
|
|
375
392
|
[--due-date YYYY-MM-DD] [--confirm --approval-token TOKEN]
|
|
376
393
|
recess [--json] memories context --student <kid-id>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// A mirror of `apps/web-server/src/services/os-v2/upload-names.ts` — the one
|
|
2
|
+
// rule for what a goal workspace stores an upload under (PROD-983). Read that
|
|
3
|
+
// module for why the rule is what it is; change it there first, then here.
|
|
4
|
+
//
|
|
5
|
+
// A copy, not an import, because `recess-cli` is published to npm from this
|
|
6
|
+
// directory's own `tsc` output and every workspace package it could import the
|
|
7
|
+
// rule from is private, so a `workspace:*` dependency would leave
|
|
8
|
+
// `npm i -g recess-cli` unable to resolve it. `skills-cache.ts` and
|
|
9
|
+
// `skill-update.ts` mirror web-server code for the same reason.
|
|
10
|
+
/**
|
|
11
|
+
* The name a workspace stores an upload under: safe for a Mesa path, stable,
|
|
12
|
+
* idempotent, and total — never empty, never `.` or `..`, never a path
|
|
13
|
+
* separator, never a dotfile.
|
|
14
|
+
*/
|
|
15
|
+
export function sanitizeWorkspaceUploadName(original) {
|
|
16
|
+
const cleaned = original.replace(/^\.+/, "").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
17
|
+
return cleaned || "file";
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=upload-names.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recess-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "Safe Recess administration and family AI tools from the command line.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -9,6 +9,13 @@
|
|
|
9
9
|
"directory": "apps/admin-cli"
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
"./engine": {
|
|
14
|
+
"development": "./src/engine.ts",
|
|
15
|
+
"types": "./src/engine.ts",
|
|
16
|
+
"default": "./dist/engine.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
12
19
|
"bin": {
|
|
13
20
|
"recess": "dist/index.js"
|
|
14
21
|
},
|
|
@@ -50,7 +50,7 @@ Never print or paste session cookies, device codes, or config-file contents.
|
|
|
50
50
|
|
|
51
51
|
## Agent contract
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
With the local binary, always pass `--json`. Stdout contains one object:
|
|
54
54
|
|
|
55
55
|
- Success: `{"ok":true,"data":{...}}`, exit 0.
|
|
56
56
|
- Failure: `{"ok":false,"error":{"code":"...","message":"...","details":{...}}}`, exit 1.
|
|
@@ -70,6 +70,21 @@ Every write is two-step:
|
|
|
70
70
|
|
|
71
71
|
Any changed target, payload, local file, server revision, amount, recipient, or consequence requires a new preview and new approval. Never infer approval from the original request. If a confirmed invocation is interrupted, retry the unchanged command with the same operation key; never mint a new one for an uncertain write. Inspect recovery state with `recess --json jobs get <operation-key>`.
|
|
72
72
|
|
|
73
|
+
### Hosted MCP transport
|
|
74
|
+
|
|
75
|
+
When this skill is reached through the hosted Recess connector, use its only tool,
|
|
76
|
+
`recess_exec({argv,input?})`. `argv` starts with the command noun, omits both `recess` and `--json`,
|
|
77
|
+
and still carries `--reason`. The CLI envelope is returned in MCP `structuredContent`; a
|
|
78
|
+
`confirmation_required` envelope is expected control flow, while other failures are tool errors.
|
|
79
|
+
Start with `argv:["agent-context"]` or `argv:["help","apps"]` for the remote-only catalog. OAuth
|
|
80
|
+
already supplies the live user and scopes: never ask for cookies, profiles, local paths, or
|
|
81
|
+
`--deliver`.
|
|
82
|
+
|
|
83
|
+
Hosted writes stage the exact argv and structured payload on the server. Preview without
|
|
84
|
+
`--confirm`, show the human `error.details.preview`, then confirm with the same command path plus
|
|
85
|
+
`--confirm --operation-key <key>` and **omit `input`**. The server executes the stored bytes; changed
|
|
86
|
+
source, flags, project/version, or operation keys require a fresh preview.
|
|
87
|
+
|
|
73
88
|
## Building Studio apps (`recess apps`)
|
|
74
89
|
|
|
75
90
|
Guides build Recess Studio apps in their own editor and publish them with the CLI; Studio never
|
|
@@ -91,6 +106,21 @@ prompts a model for them.
|
|
|
91
106
|
- `recess --json apps standards "<words or notation>"` finds the CCSS standard for `manifest.json`
|
|
92
107
|
(`"grade 4 adding fractions"` → `4.NF.B.3a`…); guides rarely know the codes, look them up.
|
|
93
108
|
|
|
109
|
+
Hosted authoring keeps those same contracts but carries no folder:
|
|
110
|
+
|
|
111
|
+
- `apps scaffold [--for-todo <id>]` returns authoring instructions, the five editable starter
|
|
112
|
+
files, fork guidance, and an optional learner brief. It performs no write.
|
|
113
|
+
- Generate `input.appBundle` with exactly `skill.js`, `model.js`, `model.css`,
|
|
114
|
+
`params.schema.json`, and `params.json`, plus `contract` and `manifest`. Preview and confirm
|
|
115
|
+
`apps validate`; keep the returned project/version/build IDs and poll `apps status <build-id>`.
|
|
116
|
+
- Fix findings in the bundle. For an existing project, first `apps pull <project-id>` and preserve
|
|
117
|
+
its `baseVersion`; a stale version is a conflict, never an overwrite.
|
|
118
|
+
- `apps preview <project-id> --version <n>` returns the short-lived capability URL.
|
|
119
|
+
Preview/confirm `apps publish <project-id> --version <n>`, then poll its build. Publication sends
|
|
120
|
+
no source and reviews exactly that validated version.
|
|
121
|
+
- After publication, preview/confirm `apps assign <project-id> --student <id> [--due YYYY-MM-DD]`.
|
|
122
|
+
Assignment is intentionally separate from hosted publish.
|
|
123
|
+
|
|
94
124
|
Two ways in. If the guide names a kid or a todo, pull the analysis first and build against it;
|
|
95
125
|
otherwise build from their description:
|
|
96
126
|
|