icoa-cli 2.19.458 → 2.19.459

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.
Files changed (122) hide show
  1. package/dist/commands/ai4ctf.js +1 -1
  2. package/dist/commands/ctf4ai-demo.js +1 -1
  3. package/dist/commands/ctf4vla.js +1 -1
  4. package/dist/commands/exam.js +1 -1
  5. package/dist/commands/learn.js +1 -1
  6. package/dist/lib/py-syntax.js +1 -1
  7. package/package.json +1 -1
  8. package/dist/commands/ai4ctf.d.ts +0 -4
  9. package/dist/commands/ai4ioai.d.ts +0 -29
  10. package/dist/commands/aienv.d.ts +0 -23
  11. package/dist/commands/arena-eai.d.ts +0 -37
  12. package/dist/commands/arena.d.ts +0 -8
  13. package/dist/commands/board.d.ts +0 -2
  14. package/dist/commands/challenge.d.ts +0 -9
  15. package/dist/commands/comms.d.ts +0 -2
  16. package/dist/commands/connect.d.ts +0 -15
  17. package/dist/commands/ctf.d.ts +0 -3
  18. package/dist/commands/ctf4ai-demo.d.ts +0 -4
  19. package/dist/commands/ctf4vla.d.ts +0 -34
  20. package/dist/commands/demo2.d.ts +0 -31
  21. package/dist/commands/demo4.d.ts +0 -3
  22. package/dist/commands/demo5.d.ts +0 -3
  23. package/dist/commands/doctor.d.ts +0 -2
  24. package/dist/commands/env.d.ts +0 -5
  25. package/dist/commands/exam.d.ts +0 -13
  26. package/dist/commands/files.d.ts +0 -2
  27. package/dist/commands/ioailab.d.ts +0 -1
  28. package/dist/commands/ipynb.d.ts +0 -20
  29. package/dist/commands/kaggle.d.ts +0 -12
  30. package/dist/commands/lang.d.ts +0 -2
  31. package/dist/commands/learn.d.ts +0 -16
  32. package/dist/commands/log.d.ts +0 -2
  33. package/dist/commands/note.d.ts +0 -25
  34. package/dist/commands/ref.d.ts +0 -2
  35. package/dist/commands/setup.d.ts +0 -2
  36. package/dist/commands/shell.d.ts +0 -35
  37. package/dist/commands/sim.d.ts +0 -32
  38. package/dist/commands/sprint.d.ts +0 -7
  39. package/dist/commands/teleload.d.ts +0 -8
  40. package/dist/commands/theme.d.ts +0 -13
  41. package/dist/commands/view.d.ts +0 -2
  42. package/dist/index.d.ts +0 -2
  43. package/dist/lib/access.d.ts +0 -14
  44. package/dist/lib/aienv.d.ts +0 -118
  45. package/dist/lib/arena-edit-dynamics.d.ts +0 -55
  46. package/dist/lib/arena-submit.d.ts +0 -23
  47. package/dist/lib/banner.d.ts +0 -9
  48. package/dist/lib/bgtasks.d.ts +0 -21
  49. package/dist/lib/board-render.d.ts +0 -14
  50. package/dist/lib/budget.d.ts +0 -1
  51. package/dist/lib/card-engine.d.ts +0 -45
  52. package/dist/lib/challenge-dir.d.ts +0 -1
  53. package/dist/lib/colors.d.ts +0 -11
  54. package/dist/lib/commands.d.ts +0 -53
  55. package/dist/lib/comms.d.ts +0 -40
  56. package/dist/lib/config.d.ts +0 -8
  57. package/dist/lib/countdown.d.ts +0 -4
  58. package/dist/lib/country-lang.d.ts +0 -1
  59. package/dist/lib/ctfd-client.d.ts +0 -33
  60. package/dist/lib/deck-cooldown.d.ts +0 -7
  61. package/dist/lib/demo-exam.d.ts +0 -28
  62. package/dist/lib/demo-flags.d.ts +0 -8
  63. package/dist/lib/demo-stats.d.ts +0 -17
  64. package/dist/lib/docker-probe.d.ts +0 -45
  65. package/dist/lib/drafts.d.ts +0 -11
  66. package/dist/lib/editor-spawn.d.ts +0 -28
  67. package/dist/lib/exam-client.d.ts +0 -15
  68. package/dist/lib/exam-sandbox.d.ts +0 -15
  69. package/dist/lib/exam-setup.d.ts +0 -19
  70. package/dist/lib/exam-state.d.ts +0 -42
  71. package/dist/lib/gemini.d.ts +0 -16
  72. package/dist/lib/gpu.d.ts +0 -51
  73. package/dist/lib/http.d.ts +0 -35
  74. package/dist/lib/i18n.d.ts +0 -215
  75. package/dist/lib/input-dynamics.d.ts +0 -38
  76. package/dist/lib/integrity-snapshot.d.ts +0 -22
  77. package/dist/lib/interactive-spawn.d.ts +0 -25
  78. package/dist/lib/ipynb-input.d.ts +0 -25
  79. package/dist/lib/kaggle.d.ts +0 -115
  80. package/dist/lib/kernel-protocol.d.ts +0 -92
  81. package/dist/lib/kernel.d.ts +0 -81
  82. package/dist/lib/learn-ai.d.ts +0 -159
  83. package/dist/lib/learn-contest.d.ts +0 -141
  84. package/dist/lib/learn-curricula.d.ts +0 -274
  85. package/dist/lib/learn-i18n.d.ts +0 -8
  86. package/dist/lib/learn-input.d.ts +0 -40
  87. package/dist/lib/learn-outcome.d.ts +0 -1
  88. package/dist/lib/learn-render.d.ts +0 -107
  89. package/dist/lib/learn-replies.d.ts +0 -42
  90. package/dist/lib/learn-state.d.ts +0 -66
  91. package/dist/lib/log-sync.d.ts +0 -2
  92. package/dist/lib/logger.d.ts +0 -12
  93. package/dist/lib/main-rl.d.ts +0 -20
  94. package/dist/lib/md-term.d.ts +0 -17
  95. package/dist/lib/menu-nav.d.ts +0 -25
  96. package/dist/lib/notebook-doc.d.ts +0 -38
  97. package/dist/lib/open-file.d.ts +0 -35
  98. package/dist/lib/paper-upgrade.d.ts +0 -27
  99. package/dist/lib/platform.d.ts +0 -110
  100. package/dist/lib/py-syntax.d.ts +0 -36
  101. package/dist/lib/render-card.d.ts +0 -17
  102. package/dist/lib/repl-asker.d.ts +0 -7
  103. package/dist/lib/resume.d.ts +0 -7
  104. package/dist/lib/sample-runner.d.ts +0 -14
  105. package/dist/lib/sandbox.d.ts +0 -30
  106. package/dist/lib/secure-store.d.ts +0 -18
  107. package/dist/lib/shell-split.d.ts +0 -17
  108. package/dist/lib/sim-cooldown.d.ts +0 -14
  109. package/dist/lib/sprint-meta.d.ts +0 -72
  110. package/dist/lib/statusline.d.ts +0 -1
  111. package/dist/lib/terminal-cleanup.d.ts +0 -35
  112. package/dist/lib/theme.d.ts +0 -4
  113. package/dist/lib/token-format.d.ts +0 -41
  114. package/dist/lib/tool-man.d.ts +0 -11
  115. package/dist/lib/toolset-hash.d.ts +0 -15
  116. package/dist/lib/translation.d.ts +0 -1
  117. package/dist/lib/translations-fetcher.d.ts +0 -6
  118. package/dist/lib/ui.d.ts +0 -12
  119. package/dist/lib/update-check.d.ts +0 -1
  120. package/dist/lib/version.d.ts +0 -8
  121. package/dist/repl.d.ts +0 -4
  122. package/dist/types/index.d.ts +0 -221
@@ -1,107 +0,0 @@
1
- import type { Card, Curriculum } from './learn-curricula.js';
2
- import type { LearnState } from './learn-state.js';
3
- import type { LearnWidthMode } from '../types/index.js';
4
- import { type CardReply } from './learn-replies.js';
5
- export declare function resolveLearnWidth(cfg: {
6
- learnWidth?: LearnWidthMode;
7
- learnWide?: boolean;
8
- }): LearnWidthMode;
9
- export declare function setLearnWidth(mode: LearnWidthMode): void;
10
- export declare function learnWidthCap(mode?: LearnWidthMode): number;
11
- export declare function learnW(): number;
12
- export declare function renderWelcome(curriculum: Curriculum, state: LearnState, isNewSession: boolean): void;
13
- export declare function wrapMixed(text: string, width: number): string[];
14
- export declare function setLearnAiEnabled(on: boolean): void;
15
- export declare function setLearnPqsViewCards(cards: ReadonlySet<number>): void;
16
- export declare function setLearnHonorRanks(ranks: ReadonlyMap<number, number>): void;
17
- export declare function setLearnReplies(r: CardReply[]): void;
18
- export type HonorWallData = {
19
- totals: {
20
- gold: number;
21
- silver: number;
22
- bronze: number;
23
- };
24
- chapters: Array<{
25
- chapter: number;
26
- gold: number;
27
- silver: number;
28
- bronze: number;
29
- cards: Array<{
30
- card: number;
31
- rank: number;
32
- icon: string;
33
- score: number;
34
- since: string;
35
- }>;
36
- }>;
37
- floor?: number;
38
- };
39
- export declare function renderHonorWall(data: HonorWallData): void;
40
- export declare function renderKnowledgeCard(card: Extract<Card, {
41
- type: 'knowledge';
42
- }>, curriculum: Curriculum): void;
43
- export declare function renderCheckFeedback(card: Extract<Card, {
44
- type: 'knowledge';
45
- }>, userAnswer: 'y' | 'n', correct: boolean, premiumExplain?: boolean): void;
46
- export declare function renderPhaseComplete(curriculum: Curriculum, state: LearnState, done: {
47
- number: number;
48
- name: string;
49
- cardRange: [number, number];
50
- }, next?: {
51
- number: number;
52
- name: string;
53
- cardRange: [number, number];
54
- }, pqsScores?: Array<{
55
- card: number;
56
- score: number;
57
- tier: string;
58
- }>, gatePhase?: {
59
- configured: boolean;
60
- contest?: {
61
- passed: boolean;
62
- best_ac?: number;
63
- need?: number;
64
- n?: number;
65
- attempts?: number;
66
- slots?: Array<{
67
- slot: number;
68
- passed: boolean;
69
- best_ac: number;
70
- need: number;
71
- n: number;
72
- attempts: number;
73
- }>;
74
- };
75
- research?: {
76
- good_cards: number;
77
- need_cards: number;
78
- min_score: number;
79
- passed: boolean;
80
- };
81
- }): void;
82
- export declare function renderCurriculumComplete(curriculum: Curriculum, state: LearnState): void;
83
- export declare function renderMCQCard(card: Extract<Card, {
84
- type: 'mcq';
85
- }>, curriculum: Curriculum): void;
86
- export declare function renderMCQFeedback(card: Extract<Card, {
87
- type: 'mcq';
88
- }>, selected: 'A' | 'B' | 'C' | 'D', correct: boolean, state: LearnState): void;
89
- export declare function renderPracticalCard(card: Extract<Card, {
90
- type: 'practical';
91
- }>, curriculum: Curriculum): void;
92
- export declare function renderPracticalSuccess(card: Extract<Card, {
93
- type: 'practical';
94
- }>): void;
95
- export declare function renderSimDemoCard(card: Extract<Card, {
96
- type: 'sim_demo';
97
- }>, curriculum: Curriculum): void;
98
- export declare function renderArenaTaskCard(card: Extract<Card, {
99
- type: 'arena_task';
100
- }>, curriculum: Curriculum): void;
101
- export declare function renderComingSoonCard(card: Extract<Card, {
102
- type: 'coming_soon';
103
- }>, curriculum: Curriculum): void;
104
- export declare function renderMilestone(card: Extract<Card, {
105
- type: 'milestone';
106
- }>, curriculum: Curriculum): void;
107
- export declare function renderStatus(curriculum: Curriculum, state: LearnState): void;
@@ -1,42 +0,0 @@
1
- /**
2
- * learn-replies — pure functions for the card human-reply channel (student
3
- * presses `e` to report a card problem; a human writes one reply; the
4
- * student reads it with `r` under that card). No chalk, no I/O — mirrors
5
- * the board-render.ts pure-module pattern. Every user-facing string ships
6
- * both EN and ZH; this channel never promises a reply timeline.
7
- */
8
- export type CardReply = {
9
- id: number;
10
- curriculumId: string;
11
- cardNumber: number;
12
- myText: string;
13
- sentAt: string;
14
- replyText?: string;
15
- repliedBy?: string;
16
- repliedAt?: string;
17
- seen: boolean;
18
- };
19
- export declare function sanitizeReplyText(s: string): string;
20
- export declare function parseReplies(raw: unknown): CardReply[];
21
- /** Local calendar day `MM-DD` — never a "3 days ago" delta (learn-continue lesson). */
22
- export declare function shortDay(iso: string): string;
23
- export declare function repliesForCard(all: CardReply[], card: number): CardReply[];
24
- /**
25
- * Recognizes an `r` / `reply` / `replies` command line — bare (current card)
26
- * or with an argument (`r 17` / `reply all` / ...) — and extracts the
27
- * target. Pure so it is directly unit-testable instead of only probeable by
28
- * grepping the regex literal out of `learn.ts`'s source text (review finding
29
- * I3, 2026-07-30: that grep went stale during an earlier fix round and the
30
- * test silently degraded to vacuous). `learn.ts`'s dispatch calls this exact
31
- * function — it is the real guard, not a copy of it.
32
- *
33
- * Returns `null` for anything that is not a reply command, including
34
- * look-alikes (`read 3`, `rally`, `r x`).
35
- */
36
- export declare function parseReplyCommand(input: string): {
37
- target: number | 'all' | 'current';
38
- } | null;
39
- export declare function unreadReplies(all: CardReply[]): CardReply[];
40
- export declare function replyPushLine(all: CardReply[], zh: boolean): string | null;
41
- export declare function replyChipLabel(all: CardReply[], card: number, zh: boolean): string | null;
42
- export declare function renderReplyLines(all: CardReply[], card: number | 'all', zh: boolean, width: number): string[];
@@ -1,66 +0,0 @@
1
- export type MCQResult = {
2
- answer: 'A' | 'B' | 'C' | 'D';
3
- correct: boolean;
4
- submittedAt: string;
5
- };
6
- export type CheckResult = {
7
- answer: 'y' | 'n';
8
- correct: boolean;
9
- submittedAt: string;
10
- };
11
- export type LearnState = {
12
- token: string;
13
- curriculumId: string;
14
- currentCard: number;
15
- totalCards: number;
16
- startedAt: string;
17
- lastSeenAt: string;
18
- streakDays: number;
19
- longestStreak: number;
20
- cardsCompleted: number[];
21
- mcqResults: Record<string, MCQResult>;
22
- checkResults?: Record<string, CheckResult>;
23
- practicalsCompleted: number[];
24
- bookmarks: number[];
25
- achievements: string[];
26
- totalSecondsActive: number;
27
- };
28
- /** Courses listed on the Learn picker. The rest need `c <track>` or `c all`. */
29
- export declare const CONTINUE_LIST_MAX = 5;
30
- /**
31
- * Every remembered course, most-recently-seen first.
32
- *
33
- * The active session comes from learn-state.json; a same-token copy left in the
34
- * archive is ignored (see `activateSession` — promotion deliberately does not
35
- * delete, so the active file always wins).
36
- */
37
- export declare function listLearnSessions(): LearnState[];
38
- /**
39
- * Switch the active course to `token`.
40
- *
41
- * Archives whichever course is active now, then returns this token's stored
42
- * progress if we have ever seen it (the caller writes it back via
43
- * `saveLearnState`, keeping a single writer for learn-state.json). Returns null
44
- * for a course this machine has never opened — the caller makes a fresh state.
45
- *
46
- * Promotion does NOT delete the archived copy. Deleting would open a window
47
- * where a crash between this call and the caller's `saveLearnState` loses the
48
- * course from both files; leaving a stale copy is harmless because the lookup
49
- * rule always prefers the active file for the active token.
50
- */
51
- export declare function activateSession(token: string): LearnState | null;
52
- export declare function loadLearnState(): LearnState | null;
53
- export declare function saveLearnState(state: LearnState): void;
54
- export declare function newLearnState(token: string, curriculumId: string, totalCards: number): LearnState;
55
- /**
56
- * Update streak based on last-seen-at vs today.
57
- * - Same day: streak unchanged
58
- * - Next day: streak + 1
59
- * - Gap > 1 day: streak resets to 1
60
- */
61
- export declare function updateStreak(state: LearnState): void;
62
- export declare function markCardComplete(state: LearnState, cardNumber: number): void;
63
- export declare function recordMCQ(state: LearnState, cardNumber: number, result: MCQResult): void;
64
- export declare function recordCheck(state: LearnState, cardNumber: number, result: CheckResult): void;
65
- export declare function markPracticalComplete(state: LearnState, cardNumber: number): void;
66
- export declare function addAchievement(state: LearnState, badge: string): void;
@@ -1,2 +0,0 @@
1
- export declare function startLogSync(): void;
2
- export declare function stopLogSync(): void;
@@ -1,12 +0,0 @@
1
- import type { LogEntry } from '../types/index.js';
2
- /** Test-only: force the next currentLastMac() call to re-seed from the log
3
- * tail, simulating a fresh process picking up an existing session.log. */
4
- export declare function __resetChainHeadForTest(): void;
5
- export declare function chainMac(prevMac: string, entry: object, key: Buffer): string;
6
- export declare function verifyChain(entries: Array<Record<string, unknown>>, startMac: string, key: Buffer): {
7
- ok: boolean;
8
- brokenAt: number;
9
- };
10
- export declare function logCommand(command: string): void;
11
- export declare function logSubmission(challengeId: number, flag: string): void;
12
- export declare function getSessionLog(): LogEntry[];
@@ -1,20 +0,0 @@
1
- /**
2
- * Shared reference to the main REPL's readline.Interface.
3
- *
4
- * Linear-flow commands (demo2, future others) need to PAUSE the main
5
- * rl while reading their own input — otherwise two readline interfaces
6
- * on the same TTY both echo every keystroke ("lang" → "llaanngg") and
7
- * both fire 'line' events.
8
- *
9
- * Pattern:
10
- * 1. startRepl() calls setMainRl(rl) right after creating its rl.
11
- * 2. Linear-flow command on entry: getMainRl()?.pause()
12
- * 3. Linear-flow command on exit: getMainRl()?.resume()
13
- *
14
- * Side note: when called from `icoa demo2` direct (no REPL parent),
15
- * getMainRl() returns null and pause/resume are no-ops. Correct
16
- * behaviour either way.
17
- */
18
- import type { Interface as ReadlineInterface } from 'node:readline';
19
- export declare function setMainRl(rl: ReadlineInterface | null): void;
20
- export declare function getMainRl(): ReadlineInterface | null;
@@ -1,17 +0,0 @@
1
- /**
2
- * md-term — tiny markdown → ANSI renderer for `icoa ipynb` markdown cells.
3
- * Deliberately minimal (headers, bold/italic, inline + fenced code, lists,
4
- * quotes, rules, links); full CommonMark is a non-goal — an unrecognized line
5
- * degrades to plain styled text, never an error. chalk-only styling per the
6
- * terminal color rules (black background — no default-color text), UTF-8
7
- * box/bullet chars only (4-platform baseline, no images/sixel).
8
- */
9
- /**
10
- * Render markdown source to display lines (already indented + chalk-styled).
11
- * Pure w.r.t. the terminal: caller prints. `width` bounds rules/underlines.
12
- */
13
- export declare function renderMarkdown(src: string, indent?: string, width?: number): string[];
14
- /** Heuristic: does this (failed-as-Python) source look like markdown/prose?
15
- * Markdown markers, or a plain multi-word line with no code-ish characters.
16
- * Only consulted AFTER a SyntaxError, so a false positive costs one gray hint. */
17
- export declare function looksLikeMarkdown(src: string): boolean;
@@ -1,25 +0,0 @@
1
- /**
2
- * Exit code the worker uses to ask the supervisor (in index.ts) to relaunch a
3
- * fresh CLI process. Picked from the unreserved 64–113 sysexits range so it
4
- * can't collide with a normal `process.exit(0|1)`. See index.ts for the
5
- * supervisor loop that consumes it.
6
- */
7
- export declare const RELAUNCH_CODE = 75;
8
- interface RlLike {
9
- close: () => void;
10
- removeAllListeners?: (event?: string) => unknown;
11
- }
12
- export declare function registerRealExit(fn: (code?: number) => never): void;
13
- /**
14
- * One-line standalone hint for sub-REPLs and mode landing screens.
15
- * Prints a friendly cue that `menu` is always available.
16
- */
17
- export declare function printMenuHint(): void;
18
- /**
19
- * Inline fragment for embedding in existing Tip / footer lines.
20
- * Returns a chalk-styled string like: menu returns home
21
- * so callers can splice it with ` · ` separators.
22
- */
23
- export declare function menuHintInline(): string;
24
- export declare function returnToMainMenu(rl?: RlLike): void;
25
- export {};
@@ -1,38 +0,0 @@
1
- /**
2
- * notebook-doc — the pure cell model behind `icoa ipynb`'s cell-stepper
3
- * (cells / show / edit / run / save). nbformat v4 is plain JSON, so parse and
4
- * serialize live here with zero dependencies; keeping the module pure (no I/O,
5
- * no chalk) means the whole document lifecycle is unit-tested without a kernel.
6
- *
7
- * Round-trip safety: cells loaded from disk keep their original outputs in
8
- * `rawOutputs` and are written back verbatim on save unless the cell was
9
- * re-run this session (then the fresh FoldedCell result wins).
10
- */
11
- import type { FoldedCell } from './kernel-protocol.js';
12
- export type CellKind = 'code' | 'markdown';
13
- export interface NbCell {
14
- kind: CellKind;
15
- source: string;
16
- /** Result of running the cell THIS session; null = not run yet. */
17
- result: FoldedCell | null;
18
- /** nbformat outputs as loaded from disk (preserved on save if not re-run). */
19
- rawOutputs?: unknown[];
20
- rawExecCount?: number | null;
21
- }
22
- /** Parse a .ipynb (nbformat v4) JSON string. Raw cells are dropped. */
23
- export declare function parseIpynb(json: string): NbCell[];
24
- /**
25
- * Split a Python script into cells on `# %%` markers (jupytext percent
26
- * format; the arena starter uses the same markers). Marker lines are
27
- * separators and are dropped. No markers → the whole file is one cell.
28
- */
29
- export declare function parsePercentPy(text: string): NbCell[];
30
- /** Serialize cells to a Jupyter-openable nbformat v4.5 JSON string. */
31
- export declare function serializeIpynb(cells: NbCell[]): string;
32
- /** One-char run state for the `cells` listing. */
33
- export declare function statusChar(cell: NbCell): string;
34
- /** First non-blank line, trimmed and truncated to `width` (with ellipsis). */
35
- export declare function cellPreview(cell: NbCell, width: number): string;
36
- /** Parse `run` arguments: '3' → [3], '1-5' → [1..5], 'all' → [1..count].
37
- * 1-based, validated against count; null = not a valid range. */
38
- export declare function parseRunRange(arg: string, count: number): number[] | null;
@@ -1,35 +0,0 @@
1
- export interface OpenCommand {
2
- cmd: string;
3
- args: string[];
4
- }
5
- /** Decide the OS "open" command for `path`. Pure — platform + env are injected
6
- * so every baseline target (macOS / Linux / WSL / Windows) is unit-testable. */
7
- export declare function openFileCommand(path: string, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): OpenCommand;
8
- /**
9
- * Open `path` in the OS default app (detached). Returns false when we could not
10
- * launch an opener — callers MUST surface the path themselves in that case.
11
- *
12
- * 🔴 spawn() does NOT throw when the binary is missing. ENOENT arrives as an
13
- * asynchronous 'error' event on the next tick, so a try/catch around spawn()
14
- * catches nothing, and with no 'error' listener Node re-throws it as an
15
- * UNCAUGHT EXCEPTION and dumps a full multi-block stack trace. That is exactly
16
- * what a WSL student saw instead of the demo2 clip (2026-07-28): Ubuntu's WSL
17
- * rootfs ships no xdg-utils, so `xdg-open` is absent on every fresh box.
18
- *
19
- * Two guards, both required:
20
- * 1. whichBinary() — a PATH scan BEFORE spawning, so "no opener" is an
21
- * honest synchronous false instead of a crash several ticks later.
22
- * 2. an 'error' listener anyway — the PATH scan can still lose a race
23
- * (binary removed, not executable), and one un-listened event kills icoa.
24
- */
25
- export declare function openFile(path: string): boolean;
26
- /** The line to print when no opener exists. Pure so every baseline target is
27
- * unit-testable; WSL gets the Windows path because `\\wsl.localhost\...` is
28
- * what a student can actually paste into Explorer. */
29
- export declare function openFallbackHint(path: string, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
30
- /**
31
- * Open a rendered clip/figure and ALWAYS tell the student what happened.
32
- * Silence is the bug: on a box with no opener the old code either said
33
- * "Opening in your default video player..." and did nothing, or crashed.
34
- */
35
- export declare function openMedia(path: string, log: (line: string) => void): boolean;
@@ -1,27 +0,0 @@
1
- /**
2
- * Paper C → Paper B upgrade prompt.
3
- *
4
- * Shown after a successful Paper C submission. Paper C is the K-12 entry
5
- * funnel (MCQ only, no tools). Paper B is the next step — adds AI4CTF
6
- * and CTF4AI practical challenges for 150-point total. The upgrade path
7
- * depends on which platform the student is on.
8
- *
9
- * Policy:
10
- * - Only triggered after Paper C submission (examId ends in `-c` or
11
- * starts with a country code + `-2026-c`).
12
- * - Only triggered if student passed. Students who failed C should
13
- * retry C before being pushed toward a harder paper.
14
- * - Always points at https://icoa2026.au/selectionguide/ for detailed
15
- * platform-specific install instructions, so the CLI copy stays
16
- * concise and the canonical guide lives in one place.
17
- * - Never prescribes a token — students must request one from their
18
- * organizer. This prevents the CLI from implying self-service token
19
- * issuance.
20
- */
21
- /**
22
- * Renders the C→B upgrade prompt to stdout. No-op if conditions aren't met.
23
- *
24
- * @param examId The submitted exam ID (from state.session.examId before clear)
25
- * @param passed Whether the student met the passing bar
26
- */
27
- export declare function showCToBUpgradePrompt(examId: string | undefined, passed: boolean): void;
@@ -1,110 +0,0 @@
1
- /**
2
- * Platform detection primitives — 3 exported probes:
3
- * isInWSL() — running inside a WSL distribution
4
- * isNativeWindowsCmd() — cmd.exe or PowerShell, NOT WSL
5
- * hasPython() — any python3 / python / py binary reachable
6
- *
7
- * Callers (repl.ts menu, exam.ts setup gate, exam.ts peek logic) inline
8
- * the policy decisions — e.g. `!isNativeWindowsCmd()` for "show exam setup"
9
- * and `isNativeWindowsCmd()` for "recommend C paper". This file stays as a
10
- * thin detection layer; policy lives with its caller.
11
- *
12
- * Context: Windows middle-school students (12-14 y/o) are an expected cohort
13
- * for ICOA 2026. Observed in-field: students blocked by WSL install complexity.
14
- * Windows native cmd + Node.js handles MCQ + AI chat but not Q31-36 practical
15
- * (Unix grep / strings / pwntools). Decision (v2.19.97 + v2.19.110): C paper
16
- * is the cmd / setup-less entry funnel. See the peek-based gate in exam.ts.
17
- */
18
- /**
19
- * Heuristic: true when running inside a WSL distribution. Node.js reports
20
- * `process.platform === 'linux'` in that case, but WSL sets specific env vars.
21
- */
22
- export declare function isInWSL(): boolean;
23
- /**
24
- * True when the user is on Windows native (cmd.exe or PowerShell), NOT a
25
- * WSL distribution.
26
- */
27
- export declare function isNativeWindowsCmd(): boolean;
28
- /**
29
- * True on Windows Node, full stop — no WSL env override.
30
- *
31
- * Inside a WSL distribution Node reports `process.platform === 'linux'`, so
32
- * `win32` on its own already means "not WSL". Prefer this over
33
- * isNativeWindowsCmd() wherever a wrong answer HIDES a diagnosis: a Windows
34
- * user who has set WSLENV on the Windows side (a documented way to configure
35
- * env passing into WSL) trips isInWSL() and would be told they are on Linux.
36
- * That is how the arena `/edit` "vi not found" message stayed wrong.
37
- */
38
- export declare function isNativeWindows(): boolean;
39
- /**
40
- * The "wrong build" trap: a WINDOWS icoa launched from inside a WSL shell.
41
- *
42
- * WSL appends the Windows PATH by default (`appendWindowsPath=true`), and a
43
- * Windows `npm i -g` writes THREE shims — `icoa.cmd`, `icoa.ps1`, and an
44
- * extensionless sh script `icoa` that bash will happily execute. So a student
45
- * who installed icoa with Windows npm and types `icoa` inside Ubuntu runs the
46
- * Windows build: the shell is Linux, the process is win32.
47
- *
48
- * Signature: platform is win32 while cwd is the WSL share. Windows resolves a
49
- * WSL directory as `\\wsl.localhost\<distro>\...` (older builds: `\\wsl$\`),
50
- * which no genuine Windows working directory looks like.
51
- *
52
- * Matters because the advice inverts: "go run it in Ubuntu" is a dead end for
53
- * someone already in Ubuntu — they need to install icoa on the Linux side.
54
- */
55
- export declare function isWindowsBuildInWslShell(cwd?: string, plat?: NodeJS.Platform): boolean;
56
- /**
57
- * Every absolute path `bin` could occupy on PATH, in PATH order. Pure — takes
58
- * env + platform so the win32 branch is unit-testable from a posix box.
59
- *
60
- * Windows needs PATHEXT expansion (`vi` never matches a bare `vi`; it would be
61
- * `vi.exe` / `vi.cmd`) and `;` as the PATH delimiter, so the separator and the
62
- * suffix set BOTH have to follow `plat`, not the host.
63
- */
64
- export declare function binaryCandidates(bin: string, env?: NodeJS.ProcessEnv, plat?: NodeJS.Platform): string[];
65
- /**
66
- * True when `bin` is reachable on PATH. A PATH scan, NOT a shell probe: the
67
- * old `execSync('command -v vi')` idiom is a POSIX-shell builtin, and on
68
- * Windows `execSync` routes through cmd.exe where `command` does not exist —
69
- * so it reported EVERY binary as missing regardless of what was installed.
70
- * (That is the v2.19.42x arena `/edit` "vi not found" false negative.)
71
- */
72
- export declare function whichBinary(bin: string, env?: NodeJS.ProcessEnv, plat?: NodeJS.Platform): boolean;
73
- /**
74
- * Probe whether any Python 3 binary is reachable. Silent — no stderr leaks
75
- * to the user. Used to decide whether Q31-35 practical questions are even
76
- * theoretically doable on this machine.
77
- *
78
- * Uses execFileSync (no shell) so probe failures don't leak cmd.exe's
79
- * "not recognized as internal or external command" messages to stdout.
80
- */
81
- export declare function hasPython(): boolean;
82
- /**
83
- * Detect duplicate icoa installations on PATH.
84
- *
85
- * Real-world failure mode (v2.19.103 → 2.19.111 upgrade incident, 2026-05-05):
86
- * a contestant's box had Node.js installed twice over time — first via NodeSource
87
- * (npm prefix /usr/local), later via apt (npm prefix /usr). The latest
88
- * `sudo npm install -g icoa-cli@latest` updated /usr/lib/... but PATH ordering
89
- * (/usr/local/bin > /usr/bin) meant `which icoa` still resolved to the
90
- * stale /usr/local copy. Banner stayed at "8 versions behind" forever; the
91
- * student / coordinator pinged the project lead.
92
- *
93
- * This probe surfaces the condition at startup so the user can resolve it
94
- * themselves. Runs `which -a icoa` (POSIX-only — skipped on Windows native).
95
- * For each result, attempts to read its package.json version so the banner
96
- * can clearly mark "older / newer / same".
97
- *
98
- * Returns an array of { path, version? } in PATH order. The FIRST entry is
99
- * what the shell currently runs. Empty/undefined version means the package
100
- * couldn't be resolved (broken symlink, atypical install layout).
101
- *
102
- * Performance: ~5ms total (one `which` call + 1-2 small file reads).
103
- * Performance budget at startup is generous; this stays well within it.
104
- */
105
- export interface IcoaInstall {
106
- path: string;
107
- version?: string;
108
- pkgDir?: string;
109
- }
110
- export declare function detectIcoaInstalls(): IcoaInstall[];
@@ -1,36 +0,0 @@
1
- export interface SyntaxEntry {
2
- /** lookup keys — first one is the display title */
3
- keys: string[];
4
- en: string;
5
- zh: string;
6
- /** runnable demo; language-neutral, `# →` marks the result */
7
- demo: string[];
8
- }
9
- export declare const SYNTAX_TABLE: SyntaxEntry[];
10
- /** Flat index of primary keys — what `/syntax` with no argument shows. */
11
- export declare const SYNTAX_INDEX: string[];
12
- /**
13
- * Exact-then-token match against the curated table.
14
- *
15
- * 🔴 Single-word keys match WHOLE TOKENS ONLY — never as a substring. A plain
16
- * `q.includes(k)` let the 2-char key `in` (the set entry) fire inside
17
- * "IndexError", so `/syntax IndexError` answered with set operations instead of
18
- * falling through to pydoc. Caught by the live arena PTY walk, not by the unit
19
- * test — the table is exactly where a wrong-but-confident answer hurts most.
20
- * Multi-word keys ("2d list", "fast io") still match as a phrase.
21
- */
22
- export declare function lookupLocal(query: string): SyntaxEntry | null;
23
- /** Nearest primary keys for a miss — suggestions, never a model. */
24
- export declare function nearKeys(query: string, limit?: number): string[];
25
- export interface PydocHit {
26
- title: string;
27
- lines: string[];
28
- }
29
- /** Ask the student's OWN interpreter. Returns null on any miss/error. */
30
- export declare function lookupPydoc(py: string, query: string, maxLines?: number): Promise<PydocHit | null>;
31
- /**
32
- * Render one lookup. All output is chalked (black-background rule). `zh`
33
- * selects the gloss language; demo code is never translated.
34
- */
35
- export declare function renderSyntax(hit: SyntaxEntry, zh: boolean): void;
36
- export declare function renderPydoc(hit: PydocHit, zh: boolean): void;
@@ -1,17 +0,0 @@
1
- import type { Curriculum } from './learn-curricula.js';
2
- export type RenderCaps = {
3
- tier: 'T0' | 'T1';
4
- cols: number;
5
- utf8: boolean;
6
- };
7
- export declare function detectCaps(): RenderCaps;
8
- /**
9
- * Render chart art for a list of chart_ids. Each chart prints on its own
10
- * "block" with a blank separator line above. Lines are left-padded with the
11
- * three-sided-frame left bar to match learn-render body style.
12
- *
13
- * Pass `frameLeft = false` for standalone (non-card-frame) contexts.
14
- */
15
- export declare function renderCharts(chartIds: string[] | undefined, charts: Curriculum['charts'], caps?: RenderCaps, opts?: {
16
- frameLeft?: boolean;
17
- }): void;
@@ -1,7 +0,0 @@
1
- export type Asker = (q: string) => Promise<string | null>;
2
- export interface AskerSession {
3
- ask: Asker;
4
- /** Restore the main REPL's 'line' listeners (or close the standalone rl). */
5
- close: () => void;
6
- }
7
- export declare function makeReplAsker(): AskerSession;
@@ -1,7 +0,0 @@
1
- export interface ResumePoint {
2
- kind: 'exam' | 'learn' | 'demo';
3
- label: string;
4
- command: string[];
5
- urgent: boolean;
6
- }
7
- export declare function resolveResumePoint(): ResumePoint | null;
@@ -1,14 +0,0 @@
1
- export declare const RUNNER_PY: string;
2
- /**
3
- * Write the runner to ~/.icoa/bin/icoa-judge.py (+ a `icoa-judge` shell shim on POSIX).
4
- * Idempotent: rewrites only if missing or stale (older version line). Stdlib-only —
5
- * needs nothing installed beyond a working python3 the student already has.
6
- */
7
- export declare function placeSampleRunner(): {
8
- ok: boolean;
9
- path: string;
10
- updated: boolean;
11
- why?: string;
12
- };
13
- /** Is a current copy of the runner present? (for `env` status display) */
14
- export declare function sampleRunnerInstalled(): boolean;
@@ -1,30 +0,0 @@
1
- /**
2
- * Sandbox images, newest first. `2026.1` bakes the aienv ML venv (core+data+
3
- * physics groups, mujoco==3.9.0 judge-parity pin) at ~/.icoa/aienv — the same
4
- * path `aienv setup` provisions on a host — so arena / self_check / notebook
5
- * flows work inside the container without teams self-installing an ML stack
6
- * that drifts from the server judge (the 2026-07 finals feedback). The legacy
7
- * `2026` tag stays as a fallback so the sandbox keeps working for users who
8
- * only have (or can only reach) the old image.
9
- */
10
- export declare const SANDBOX_IMAGES: readonly ["icoa/sandbox:2026.1", "icoa/sandbox:2026"];
11
- /**
12
- * Pick the sandbox image: any LOCAL image first (offline-graceful — never block
13
- * a session on a multi-GB pull when a working sandbox is already on disk), then
14
- * pull newest-first. null → caller decides the fallback (host shell / local
15
- * build). Probes are injected so the ladder stays pure + unit-tested (tests
16
- * live in test/shell-sandbox-mounts.test.js via the shell.ts re-export).
17
- */
18
- export declare function resolveSandboxImage(hasLocal: (image: string) => boolean, pull: (image: string) => boolean, images?: readonly string[]): string | null;
19
- /**
20
- * Is Docker usable right now? Probes the daemon socket directly (never the
21
- * `docker` CLI) so a stopped Docker Desktop is NOT auto-started — the sandbox
22
- * is optional and the host shell is the fallback, so merely *checking* must not
23
- * wake Docker. See lib/docker-probe.ts.
24
- */
25
- export declare function isDockerAvailable(): Promise<boolean>;
26
- export declare function ensureSandbox(): Promise<boolean>;
27
- export declare function runInSandbox(command: string, rl: {
28
- pause: () => void;
29
- resume: () => void;
30
- }): Promise<void>;
@@ -1,18 +0,0 @@
1
- export type IntegrityVerdict = 'ok' | 'tampered' | 'unsigned' | 'absent' | 'corrupt';
2
- export interface SigBlock {
3
- v: 1;
4
- alg: 'HS256';
5
- mac: string;
6
- ts: string;
7
- }
8
- export declare function deriveFileKey(installSalt: string, fileId: string): Buffer;
9
- export declare function canonicalize(obj: unknown): string;
10
- export declare function signObject(payload: object, key: Buffer): SigBlock;
11
- export declare function verifyObject(payloadWithSig: object, key: Buffer): IntegrityVerdict;
12
- export declare function writeSignedFile(path: string, payload: object, key: Buffer): void;
13
- export declare function readSignedFile<T>(path: string, key: Buffer): {
14
- data: T | null;
15
- verdict: IntegrityVerdict;
16
- };
17
- export declare function recordVerdict(fileId: string, verdict: IntegrityVerdict): void;
18
- export declare function collectVerdicts(): Record<string, IntegrityVerdict>;