litmus-cli 1.4.7 → 1.4.9

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.
@@ -101,7 +101,7 @@ export function resolvePythonInterpreter() {
101
101
  * which is the case a warning most needs to catch. `shell: true` on Windows so
102
102
  * `.cmd`/`.ps1` shims resolve, matching the Python probe above.
103
103
  */
104
- function isOnPath(bin) {
104
+ export function isOnPath(bin) {
105
105
  try {
106
106
  return (spawnSync(bin, ["--version"], {
107
107
  encoding: "utf8",
@@ -116,7 +116,8 @@ function isOnPath(bin) {
116
116
  /**
117
117
  * Detect project type and suggest an install command.
118
118
  */
119
- export function detectProject(dir) {
119
+ export function detectProject(dir, deps = {}) {
120
+ const onPath = deps.isOnPath ?? isOnPath;
120
121
  if (existsSync(path.join(dir, "package.json"))) {
121
122
  const hasYarnLock = existsSync(path.join(dir, "yarn.lock"));
122
123
  const hasPnpmLock = existsSync(path.join(dir, "pnpm-lock.yaml"));
@@ -133,24 +134,13 @@ export function detectProject(dir) {
133
134
  : hasBunLock
134
135
  ? "bun install"
135
136
  : "npm install";
136
- // Same disclosure contract as the Python skew below: a missing toolchain is
137
- // ours to state here, not theirs to infer. Without this, `init`'s install
138
- // failure prints "run `bun install` manually", which is the command that just
139
- // failed — and the candidate is left guessing that a runtime they may never
140
- // have heard of is the missing piece.
141
- //
142
137
  // Gated on the SELECTED command, not on `hasBunLock`. A repo carrying both a
143
- // bun lockfile and a pnpm/yarn one resolves to pnpm/yarn above, and telling
144
- // that candidate to install Bun and run `bun install` would send them to
145
- // install a second, competing dependency tree over the one we just built.
146
- // Also means npm/yarn/pnpm repos never pay for the PATH probe.
147
- const toolchainWarning = installCommand === "bun install" && !isOnPath("bun")
148
- ? "This assessment's scaffold is a Bun project (bun.lock), and Bun isn't on " +
149
- "your PATH. Install it from https://bun.sh, then re-run `bun install` in " +
150
- "this folder. npm can install the dependencies but cannot run the " +
151
- "scaffold's server — it uses Bun-only APIs."
152
- : undefined;
153
- return { type: "node", installCommand, toolchainWarning };
138
+ // bun lockfile and a pnpm/yarn one resolves to pnpm/yarn above, and reporting
139
+ // bun as missing there would send the candidate to install a second,
140
+ // competing dependency tree over the one we just built. Also means
141
+ // npm/yarn/pnpm repos never pay for the PATH probe.
142
+ const missingRuntime = installCommand === "bun install" && !onPath("bun") ? "bun" : undefined;
143
+ return { type: "node", installCommand, missingRuntime };
154
144
  }
155
145
  if (existsSync(path.join(dir, "requirements.txt")) ||
156
146
  existsSync(path.join(dir, "pyproject.toml")) ||
@@ -203,4 +193,35 @@ export function detectProject(dir) {
203
193
  }
204
194
  return { type: "unknown", installCommand: null };
205
195
  }
196
+ /** How each supported runtime is obtained. npm ships per-platform bun binaries,
197
+ * and npm is guaranteed present because this CLI is an npm package. */
198
+ export const RUNTIME_INSTALL = {
199
+ bun: "npm install -g bun",
200
+ };
201
+ /**
202
+ * Install a runtime the project needs and the machine lacks.
203
+ *
204
+ * Separated from `init`'s reporting so all three outcomes are assertable: the
205
+ * install failing, and the install "succeeding" while leaving nothing on PATH.
206
+ * That second case is the one worth a unit test — a global install can land
207
+ * somewhere this process cannot reach, and treating it as success means the
208
+ * dependency install below dies on a command-not-found we already knew about.
209
+ */
210
+ export function installRuntime(runtime, deps = {}) {
211
+ const exec = deps.exec ?? ((command) => {
212
+ // spawnSync reports a failure in `status` rather than throwing, so surface
213
+ // it as one — the catch below is what turns it into "unavailable".
214
+ const r = spawnSync(command, { shell: true, stdio: "pipe" });
215
+ if (r.status !== 0)
216
+ throw new Error(`\`${command}\` exited ${r.status}`);
217
+ });
218
+ const onPath = deps.isOnPath ?? isOnPath;
219
+ try {
220
+ exec(RUNTIME_INSTALL[runtime]);
221
+ }
222
+ catch {
223
+ return "unavailable";
224
+ }
225
+ return onPath(runtime) ? "installed" : "unavailable";
226
+ }
206
227
  //# sourceMappingURL=detect-project.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"detect-project.js","sourceRoot":"","sources":["../../src/lib/detect-project.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"detect-project.js","sourceRoot":"","sources":["../../src/lib/detect-project.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"AAYA;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAuCf"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"AAYA;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAmEf"}
@@ -1,5 +1,5 @@
1
1
  import { createWriteStream } from "fs";
2
- import { mkdir } from "fs/promises";
2
+ import { chmod, mkdir } from "fs/promises";
3
3
  import path from "path";
4
4
  import { pipeline } from "stream/promises";
5
5
  import unzipper from "unzipper";
@@ -42,6 +42,33 @@ export async function extractZip(zipPath, targetDir) {
42
42
  }
43
43
  await mkdir(path.dirname(resolvedPath), { recursive: true });
44
44
  await pipeline(entry.stream(), createWriteStream(resolvedPath));
45
+ // RESTORE THE EXECUTABLE BIT, which `createWriteStream` drops.
46
+ //
47
+ // It writes with the default mode and ignores whatever the archive says, so
48
+ // a package whose brief instructs
49
+ //
50
+ // ./bin/train --config config/run.json --out runs/mine
51
+ //
52
+ // extracted to a non-executable file and the candidate hit "Permission
53
+ // denied" on the first thing they were told to do. Manual `unzip` honours
54
+ // the mode, so this only ever broke for people using the CLI, which is
55
+ // everyone.
56
+ //
57
+ // GRANTED, NOT COPIED. We add +x when the archive marks the entry
58
+ // executable and clamp to 0755; we never apply the archive's mode verbatim.
59
+ // An archive can carry setuid, setgid and sticky bits, and nothing arriving
60
+ // over the wire should be able to set those on a candidate's machine.
61
+ // Anything not marked executable keeps the default write mode.
62
+ //
63
+ // The unix mode lives in the high half of the ZIP's external attributes.
64
+ // Absent on archives written by tools that store no mode, which reads as 0
65
+ // and correctly grants nothing.
66
+ const mode = (entry.externalFileAttributes ?? 0) >>> 16;
67
+ if (mode & 0o111) {
68
+ // Best-effort: on Windows chmod only toggles the read-only flag, and a
69
+ // filesystem that refuses it should not fail the whole extraction.
70
+ await chmod(resolvedPath, 0o755).catch(() => { });
71
+ }
45
72
  }
46
73
  }
47
74
  //# sourceMappingURL=extract.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,IAAI,CAAA;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,QAAQ,MAAM,UAAU,CAAA;AAE/B,6EAA6E;AAC7E,8EAA8E;AAC9E,gEAAgE;AAChE,MAAM,eAAe,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAA,CAAC,yBAAyB;AACnE,MAAM,cAAc,GAAG,MAAO,CAAA;AAE9B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,SAAiB;IAEjB,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,CAAA;IACzD,MAAM,KAAK,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAEhD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAEnD,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,+BAA+B,SAAS,CAAC,KAAK,CAAC,MAAM,iBAAiB,cAAc,yBAAyB,CAAC,CAAA;IAChI,CAAC;IAED,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAC7D,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CACb,iCAAiC,KAAK,CAAC,IAAI,4BAA4B,CACxE,CAAA;QACH,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC/B,MAAM,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAC9C,SAAQ;QACV,CAAC;QACD,yEAAyE;QACzE,uEAAuE;QACvE,4EAA4E;QAC5E,mDAAmD;QACnD,IAAK,KAAK,CAAC,IAAe,KAAK,cAAc;YAAE,SAAQ;QAEvD,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;QAC1F,UAAU,IAAI,UAAU,CAAA;QACxB,IAAI,UAAU,GAAG,eAAe,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,wCAAwC,CAAC,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,qCAAqC,CACxH,CAAA;QACH,CAAC;QACD,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC5D,MAAM,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAA;IACjE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/lib/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Bind a Python assessment's notebook to the environment `init` just built.
3
+ *
4
+ * WHY THIS EXISTS AT ALL (ENG-1824). `litmus init` has always created `venv/`
5
+ * and installed into it, and ENG-1417 made it prefer the grading interpreter
6
+ * and warn when it could not get one. None of that reaches a notebook. For an
7
+ * `.ipynb` deliverable the graded artifact is the SAVED CELL OUTPUT — the
8
+ * extractor parses the notebook as JSON and never re-executes it — so the
9
+ * interpreter that decides the result is the one Jupyter picked, not the one
10
+ * in `venv/`. A perfect venv the kernel never uses buys exactly zero, and the
11
+ * shipped notebooks carry the generic kernelspec `{"name": "python3"}`, which
12
+ * resolves to whatever `python3` kernel the machine happens to have. A
13
+ * candidate with their own JupyterLab and their own pandas therefore runs
14
+ * every cell on a foreign interpreter, and nothing anywhere notices.
15
+ *
16
+ * EVERYTHING HERE IS BEST-EFFORT AND NEVER FATAL. It runs on the path that
17
+ * decides whether a candidate can start working, against tooling we do not
18
+ * control, on three platforms. A kernel we failed to register is a candidate
19
+ * who picks one manually — today's behaviour. An exception thrown here would
20
+ * be a candidate who cannot start at all, which is far worse than the skew it
21
+ * was trying to prevent. Every entry point returns a result and swallows.
22
+ */
23
+ /** Where the venv's interpreter lives, per platform. */
24
+ export declare function venvPython(dir: string): string;
25
+ /** The `jupyter` the candidate should be told to run: the venv's own. */
26
+ export declare function venvJupyterCommand(): string;
27
+ /**
28
+ * Is `jupyter lab` actually runnable in this venv?
29
+ *
30
+ * `ipykernel` alone is not enough to answer that, and `ipykernel` is what the
31
+ * kernel registration proves. It brings in `jupyter-core`, so `venv/bin/jupyter`
32
+ * EXISTS while the `lab` subcommand — a separate `jupyterlab` distribution —
33
+ * does not. Telling a candidate to run the first and most prominent command we
34
+ * give them, and having it answer `'lab' is not a Jupyter command`, is a worse
35
+ * first thirty seconds than saying nothing.
36
+ */
37
+ export declare function hasJupyterLab(dir: string): boolean;
38
+ export declare function uvVersion(): string | null;
39
+ /**
40
+ * Build `venv/` with uv, pinned to the grading interpreter.
41
+ *
42
+ * The directory name is passed EXPLICITLY. uv's default is `.venv`, and every
43
+ * line of instruction we ship — the package README's `source venv/bin/activate`
44
+ * included — names `venv`. Taking uv's default here would leave the candidate
45
+ * reading true-looking instructions about a directory that does not exist.
46
+ */
47
+ export declare function buildVenvWithUv(dir: string): boolean;
48
+ /**
49
+ * The distributions `requirements.txt` declares, as installable names.
50
+ *
51
+ * Deliberately conservative: anything that is not a plain pinned-or-floored
52
+ * requirement (a flag, an `-r` include, a URL, a path) is dropped rather than
53
+ * guessed at, because the only consumer is a probe whose false alarms would be
54
+ * worse than its silence.
55
+ */
56
+ export declare function declaredRequirements(dir: string): string[];
57
+ export interface EnvProbe {
58
+ version: string | null;
59
+ /** Declared distributions that are not installed in the venv. */
60
+ missing: string[];
61
+ /** Top-level modules that are installed but fail to import (broken wheel/ABI). */
62
+ broken: string[];
63
+ }
64
+ /**
65
+ * Ask the venv's own interpreter what it has.
66
+ *
67
+ * PRESENCE IS CHECKED THROUGH `importlib.metadata`, NOT BY IMPORTING THE
68
+ * REQUIREMENT NAME. Distribution names and module names disagree often enough
69
+ * (`beautifulsoup4`/`bs4`, `scikit-learn`/`sklearn`, `pillow`/`PIL`) that a
70
+ * hand-maintained alias table would eventually tell a candidate their working
71
+ * environment is broken. The metadata lookup is exact. The import check is
72
+ * then driven off the metadata's own top-level modules, so it stays exact too
73
+ * while still catching the case presence cannot — a wheel that installs and
74
+ * then fails to load.
75
+ */
76
+ export declare function probeEnvironment(dir: string): EnvProbe;
77
+ /**
78
+ * A kernelspec name identifying ONE CHECKOUT, not one assessment.
79
+ *
80
+ * Jupyter lowercases kernelspec names on install and matches them literally
81
+ * afterwards, so the name is normalised HERE rather than being compared
82
+ * case-insensitively later — otherwise the notebook we rewrite points at
83
+ * `litmus-Foo` while the registered kernel is `litmus-foo`, and the candidate
84
+ * gets a picker prompt about a missing kernel, which is worse than the generic
85
+ * name we started with.
86
+ *
87
+ * THE PATH FINGERPRINT IS WHAT KEEPS TWO CHECKOUTS APART, and the name alone
88
+ * cannot: kernelspecs are user-scope and `ipykernel install` OVERWRITES a spec
89
+ * of the same name. Two folders that normalise alike — a practice run beside a
90
+ * real attempt, the same assessment re-initialised under a different parent,
91
+ * or two companies who both called theirs "Take Home" — would otherwise share
92
+ * one spec. The second init would silently redirect the first notebook at the
93
+ * second checkout's interpreter, and submitting either would delete the spec
94
+ * the other one is bound to. The fingerprint is not shown to anyone: the
95
+ * picker displays `display_name`, and the notebook is bound by name so the
96
+ * candidate never has to read either.
97
+ */
98
+ export declare function kernelNameFor(folderName: string, projectRoot: string): string;
99
+ /**
100
+ * Register the venv's interpreter as a Jupyter kernel.
101
+ *
102
+ * `--user`, NOT `--sys-prefix`. A sys-prefix kernel is visible only to a
103
+ * JupyterLab launched from inside that venv, which excludes exactly the
104
+ * candidate this whole change is for: the one who already has a global
105
+ * JupyterLab, opens the notebook with it, and cannot even SEE the correct
106
+ * kernel. A user-scope kernelspec is discoverable by any Jupyter on the
107
+ * machine, which is the property that makes the notebook rewrite below mean
108
+ * something.
109
+ *
110
+ * Requires `ipykernel` in the venv. Assessments that ship jupyterlab get it
111
+ * transitively; ones that do not simply return false and the flow degrades to
112
+ * today's behaviour.
113
+ */
114
+ export declare function registerKernel(dir: string, name: string, displayName: string): boolean;
115
+ /**
116
+ * Remove the kernel we registered, before the assessment folder goes away.
117
+ *
118
+ * A user-scope kernelspec OUTLIVES THE FOLDER, and `litmus submit` deletes the
119
+ * folder. Left behind, it is a permanent entry in the candidate's own Jupyter
120
+ * pointing at an interpreter that no longer exists — our litter on their
121
+ * machine, and a broken kernel in every notebook they open afterwards. We made
122
+ * the global change, so we undo it.
123
+ *
124
+ * Called while the venv still exists, because that is what provides
125
+ * `jupyter_client` — asking it to remove the spec is how this stays correct on
126
+ * all three platforms without hardcoding three kernel directories.
127
+ */
128
+ export declare function unregisterKernel(dir: string, name: string): boolean;
129
+ /**
130
+ * Rewrite a notebook's kernelspec name/display_name, changing nothing else.
131
+ *
132
+ * A SURGICAL STRING EDIT, NOT A PARSE-AND-SERIALISE. The obvious
133
+ * implementation — `JSON.parse`, mutate, `JSON.stringify` — reformats the
134
+ * whole file: a 900-line notebook collapses to one line with the default
135
+ * serialiser and still differs byte-for-byte under any indent setting. That
136
+ * matters here more than it usually would, because `init` git-commits the
137
+ * extracted package as the candidate's baseline and the notebook diff against
138
+ * that baseline is grading evidence. Reflowing the deliverable before the
139
+ * candidate has opened it turns every later diff into one line of churn on the
140
+ * one file that carries their work.
141
+ *
142
+ * So: find the `kernelspec` object, replace the two string values inside it,
143
+ * leave every other byte — including `language`, the trailing whitespace and
144
+ * the file's own indentation — exactly as shipped. Returns null when there is
145
+ * nothing to edit, which is a report, not a failure: a notebook without a
146
+ * kernelspec is one we should leave alone rather than restructure.
147
+ */
148
+ export declare function rewriteKernelspec(source: string, name: string, displayName: string): string | null;
149
+ /**
150
+ * Point every notebook the package shipped at the registered kernel.
151
+ *
152
+ * Scoped to notebooks that exist at init time, i.e. ones we shipped. The
153
+ * candidate's own later notebooks are theirs; the deliverable is the one that
154
+ * has to execute on the environment we built.
155
+ */
156
+ export declare function bindNotebooks(dir: string, name: string, displayName: string): string[];
157
+ //# sourceMappingURL=python-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python-env.d.ts","sourceRoot":"","sources":["../../src/lib/python-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAUH,wDAAwD;AACxD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAKlD;AAwDD,wBAAgB,SAAS,IAAI,MAAM,GAAG,IAAI,CAOzC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA0CpD;AAID;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB1D;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,kFAAkF;IAClF,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAgDtD;AAID;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAW7E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAOtF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAUnE;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CA6Df;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAoCtF"}