chesspal 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/cli.js +6 -6
- package/dist/commands/{analyse-game.d.ts → analysis/analyse.d.ts} +2 -2
- package/dist/commands/{analyse-game.js → analysis/analyse.js} +7 -11
- package/dist/commands/{annotate-game.d.ts → analysis/annotate.d.ts} +1 -1
- package/dist/commands/{annotate-game.js → analysis/annotate.js} +6 -9
- package/dist/commands/{delete-review.d.ts → analysis/delete-review.d.ts} +1 -1
- package/dist/commands/{delete-review.js → analysis/delete-review.js} +5 -5
- package/dist/commands/analysis/index.d.ts +4 -0
- package/dist/commands/analysis/index.js +5 -0
- package/dist/commands/{study-games.d.ts → analysis/study-games.d.ts} +1 -1
- package/dist/commands/{study-games.js → analysis/study-games.js} +4 -5
- package/dist/commands/{agents.d.ts → answers/agents.d.ts} +3 -3
- package/dist/commands/{agents.js → answers/agents.js} +5 -5
- package/dist/commands/{clock.d.ts → answers/clock.d.ts} +2 -2
- package/dist/commands/{clock.js → answers/clock.js} +2 -2
- package/dist/commands/{drawings.d.ts → answers/drawings.d.ts} +1 -1
- package/dist/commands/{drawings.js → answers/drawings.js} +1 -1
- package/dist/commands/answers/index.d.ts +12 -0
- package/dist/commands/answers/index.js +14 -0
- package/dist/commands/{lead-input.d.ts → answers/lead-input.d.ts} +1 -1
- package/dist/commands/{lead-input.js → answers/lead-input.js} +1 -1
- package/dist/commands/{answer-lines.d.ts → answers/lines.d.ts} +2 -2
- package/dist/commands/{answer-lines.js → answers/lines.js} +4 -4
- package/dist/commands/{opening-book.d.ts → answers/opening-book.d.ts} +1 -1
- package/dist/commands/{opening-book.js → answers/opening-book.js} +3 -3
- package/dist/commands/{play-move.d.ts → answers/play-move.d.ts} +2 -2
- package/dist/commands/{play-move.js → answers/play-move.js} +3 -3
- package/dist/commands/{say.d.ts → answers/say.d.ts} +3 -3
- package/dist/commands/{say.js → answers/say.js} +4 -4
- package/dist/commands/{speech.js → answers/speech.js} +1 -1
- package/dist/commands/{study.d.ts → answers/study.d.ts} +3 -3
- package/dist/commands/{study.js → answers/study.js} +3 -3
- package/dist/commands/{game-summary.d.ts → answers/summary.d.ts} +2 -2
- package/dist/commands/{game-summary.js → answers/summary.js} +4 -4
- package/dist/commands/{view.d.ts → answers/view.d.ts} +1 -1
- package/dist/commands/{ai-move.d.ts → game/ai-move.d.ts} +2 -2
- package/dist/commands/{ai-move.js → game/ai-move.js} +6 -11
- package/dist/commands/{coach.d.ts → game/coach.d.ts} +1 -1
- package/dist/commands/{coach.js → game/coach.js} +7 -13
- package/dist/commands/{draw-offer.d.ts → game/draw-offer.d.ts} +1 -1
- package/dist/commands/{draw-offer.js → game/draw-offer.js} +4 -4
- package/dist/commands/{end-game.d.ts → game/end.d.ts} +2 -2
- package/dist/commands/{end-game.js → game/end.js} +5 -9
- package/dist/commands/{export-game.d.ts → game/export.d.ts} +1 -1
- package/dist/commands/{export-game.js → game/export.js} +4 -4
- package/dist/commands/game/index.d.ts +11 -0
- package/dist/commands/game/index.js +13 -0
- package/dist/commands/{levels.d.ts → game/levels.d.ts} +3 -3
- package/dist/commands/{levels.js → game/levels.js} +3 -3
- package/dist/commands/{look.d.ts → game/look.d.ts} +3 -4
- package/dist/commands/{look.js → game/look.js} +5 -10
- package/dist/commands/{new-game.d.ts → game/new.d.ts} +3 -4
- package/dist/commands/{new-game.js → game/new.js} +17 -13
- package/dist/commands/{page-link.d.ts → game/page-link.d.ts} +1 -1
- package/dist/commands/{player-move.d.ts → game/player-move.d.ts} +2 -2
- package/dist/commands/{player-move.js → game/player-move.js} +4 -8
- package/dist/commands/{ponder.d.ts → game/ponder.d.ts} +3 -3
- package/dist/commands/{ponder.js → game/ponder.js} +6 -9
- package/dist/commands/{position.d.ts → game/position.d.ts} +2 -2
- package/dist/commands/{position.js → game/position.js} +5 -7
- package/dist/commands/{status.d.ts → game/status.d.ts} +2 -4
- package/dist/commands/{status.js → game/status.js} +12 -17
- package/dist/commands/index.d.ts +6 -17
- package/dist/commands/index.js +11 -38
- package/dist/commands/{delete-game.d.ts → profiles/delete-game.d.ts} +1 -1
- package/dist/commands/{delete-game.js → profiles/delete-game.js} +3 -3
- package/dist/commands/profiles/index.d.ts +4 -0
- package/dist/commands/profiles/index.js +5 -0
- package/dist/commands/{past-games.d.ts → profiles/past-games.d.ts} +1 -1
- package/dist/commands/{past-games.js → profiles/past-games.js} +5 -5
- package/dist/commands/{profiles.d.ts → profiles/profiles.d.ts} +7 -7
- package/dist/commands/{profiles.js → profiles/profiles.js} +4 -4
- package/dist/commands/{ratings.d.ts → profiles/ratings.d.ts} +1 -1
- package/dist/commands/{ratings.js → profiles/ratings.js} +2 -2
- package/dist/commands/{context.js → registry/context.js} +2 -2
- package/dist/commands/registry/index.d.ts +2 -0
- package/dist/commands/registry/index.js +3 -0
- package/dist/commands/setup/index.d.ts +2 -0
- package/dist/commands/setup/index.js +3 -0
- package/dist/commands/{init.d.ts → setup/init.d.ts} +1 -1
- package/dist/commands/{init.js → setup/init.js} +2 -3
- package/dist/commands/{instructions.d.ts → setup/instructions.d.ts} +2 -2
- package/dist/commands/{instructions.js → setup/instructions.js} +3 -3
- package/dist/commands/{ai-say.d.ts → talk/ai-say.d.ts} +2 -3
- package/dist/commands/{ai-say.js → talk/ai-say.js} +5 -9
- package/dist/commands/talk/index.d.ts +2 -0
- package/dist/commands/talk/index.js +3 -0
- package/dist/commands/{player-say.d.ts → talk/player-say.d.ts} +1 -1
- package/dist/commands/{player-say.js → talk/player-say.js} +4 -4
- package/dist/page/app/app.js +19 -11
- package/dist/page/app/served.js +7 -0
- package/dist/page/avatars/torretta.js +10 -3
- package/dist/page/dialogs/dialogs.css +10 -0
- package/dist/page/dialogs/profiles.js +1 -5
- package/dist/page/dialogs/settings.js +18 -4
- package/dist/page/game/column.js +1 -1
- package/dist/page/game/game.css +1 -1
- package/dist/page/guide/blocks.js +13 -2
- package/dist/page/guide/credits.js +6 -0
- package/dist/page/guide/drawings/analysis.js +13 -0
- package/dist/page/guide/drawings/drawings.css +329 -0
- package/dist/page/guide/drawings/drawings.js +15 -0
- package/dist/page/guide/drawings/game.js +15 -0
- package/dist/page/guide/drawings/helps.js +13 -0
- package/dist/page/guide/drawings/how-it-works.js +13 -0
- package/dist/page/guide/drawings/parts.js +103 -0
- package/dist/page/guide/drawings/players.js +19 -0
- package/dist/page/guide/drawings/talk.js +9 -0
- package/dist/page/guide/en.md +39 -25
- package/dist/page/guide/guide.css +123 -91
- package/dist/page/guide/guide.js +47 -25
- package/dist/page/guide/it.md +39 -25
- package/dist/page/guide/markdown.js +5 -0
- package/dist/page/helps/helps.css +127 -83
- package/dist/page/helps/helps.js +45 -26
- package/dist/page/helps/view.js +13 -3
- package/dist/page/{guide/foot.js → layout/footer.js} +7 -8
- package/dist/page/layout/header.js +5 -3
- package/dist/page/layout/layout.css +114 -10
- package/dist/page/layout/window.js +6 -26
- package/dist/page/main.js +1 -1
- package/dist/page/pieces/pieces.js +5 -2
- package/dist/page/style.css +1 -0
- package/dist/page/table/table.css +69 -0
- package/dist/page/table/update.js +82 -0
- package/dist/page/talk/chat.css +6 -6
- package/dist/page/talk/talk.css +17 -67
- package/dist/page/talk/talk.js +5 -5
- package/dist/page/talk/voice.js +12 -0
- package/dist/page/text.js +66 -18
- package/dist/page/theme.css +2 -0
- package/dist/server/{bell.js → bell/bell.js} +3 -3
- package/dist/server/bell/index.d.ts +1 -0
- package/dist/server/bell/index.js +2 -0
- package/dist/server/{caller.d.ts → mcp/caller.d.ts} +1 -1
- package/dist/server/{caller.js → mcp/caller.js} +1 -1
- package/dist/server/mcp/index.d.ts +1 -0
- package/dist/server/mcp/index.js +2 -0
- package/dist/server/{mcp.d.ts → mcp/mcp.d.ts} +1 -1
- package/dist/server/{mcp.js → mcp/mcp.js} +9 -9
- package/dist/server/page/game.js +16 -3
- package/dist/server/page/http.d.ts +1 -1
- package/dist/server/page/index.d.ts +3 -0
- package/dist/server/page/index.js +3 -0
- package/dist/server/page/page.d.ts +12 -3
- package/dist/server/page/page.js +13 -4
- package/dist/server/page/table.d.ts +7 -1
- package/dist/server/page/table.js +79 -3
- package/dist/server/page/update.d.ts +18 -0
- package/dist/server/page/update.js +40 -0
- package/dist/server/table/browser.d.ts +1 -1
- package/dist/server/table/browser.js +1 -1
- package/dist/server/table/hosts/chats.d.ts +1 -1
- package/dist/server/table/hosts/chats.js +2 -2
- package/dist/server/table/hosts/index.d.ts +1 -1
- package/dist/server/table/hosts/index.js +1 -1
- package/dist/server/table/hosts/play.d.ts +8 -1
- package/dist/server/table/hosts/play.js +40 -4
- package/dist/server/table/hosts/seat.d.ts +5 -1
- package/dist/server/table/hosts/seat.js +1 -1
- package/dist/server/table/index.d.ts +3 -2
- package/dist/server/table/index.js +3 -2
- package/dist/server/table/launch.d.ts +5 -2
- package/dist/server/table/launch.js +6 -3
- package/dist/server/table/link.d.ts +1 -1
- package/dist/server/table/link.js +3 -3
- package/dist/server/table/table.d.ts +4 -5
- package/dist/server/table/table.js +15 -12
- package/dist/server/table/update.d.ts +17 -0
- package/dist/server/table/update.js +77 -0
- package/dist/sites/index.d.ts +1 -1
- package/dist/sites/index.js +2 -2
- package/dist/version.d.ts +4 -0
- package/dist/version.js +12 -1
- package/package.json +1 -1
- /package/dist/commands/{speech.d.ts → answers/speech.d.ts} +0 -0
- /package/dist/commands/{view.js → answers/view.js} +0 -0
- /package/dist/commands/{page-link.js → game/page-link.js} +0 -0
- /package/dist/commands/{context.d.ts → registry/context.d.ts} +0 -0
- /package/dist/commands/{registry.d.ts → registry/registry.d.ts} +0 -0
- /package/dist/commands/{registry.js → registry/registry.js} +0 -0
- /package/dist/server/{bell.d.ts → bell/bell.d.ts} +0 -0
package/dist/page/text.js
CHANGED
|
@@ -29,6 +29,7 @@ const english = {
|
|
|
29
29
|
theme: "Light or dark theme",
|
|
30
30
|
sounds: "Sounds",
|
|
31
31
|
voice: "Read the AI's words aloud",
|
|
32
|
+
noVoice: "This browser has no voice for this language: add one in your system's settings",
|
|
32
33
|
saidMove: (move) => moveInWords(englishMoveWords, move),
|
|
33
34
|
newGame: "New game",
|
|
34
35
|
offerDraw: "Offer a draw",
|
|
@@ -147,6 +148,20 @@ const english = {
|
|
|
147
148
|
boardLabel: (turn, lastMove) => `Chessboard, ${english.toMove(turn)}${lastMove === undefined ? "" : `, last move ${lastMove}`}`,
|
|
148
149
|
clearChat: (name) => `Before a new game, clear your chat with ${name}: in the window where you opened ${name}, not here, write /clear, then a message such as "new game". A clean chat plays faster.`,
|
|
149
150
|
outdated: (name) => `Chesspal has been updated, but this folder still has its old files: close the chat with ${name}, run chesspal init in this folder, and open a new chat.`,
|
|
151
|
+
update: {
|
|
152
|
+
mark: (version) => `Chesspal ${version} is out: update`,
|
|
153
|
+
title: "Update Chesspal",
|
|
154
|
+
offer: (version, current) => `Chesspal ${version} is out${current === undefined ? "" : `; this is ${current}`}. The table installs it and starts again by itself.`,
|
|
155
|
+
chatAgain: (name) => `The chat with ${name} starts again too, clean.`,
|
|
156
|
+
byHand: (name) => `You opened the chat with ${name} yourself: afterwards, close it and open a new one.`,
|
|
157
|
+
whilePlaying: "A game is going on: finish it, then update.",
|
|
158
|
+
install: "Update",
|
|
159
|
+
installing: (version) => `Installing ${version}…`,
|
|
160
|
+
installed: "Installed: the table starts again by itself once the game is over.",
|
|
161
|
+
failed: "Chesspal was not updated: close the chat, then type in a terminal, one after the other:",
|
|
162
|
+
notice: (version, name) => `Chesspal ${version} is out: close the chat with ${name}, type this command in a terminal, then open a new chat.`,
|
|
163
|
+
hide: "Hide until the next version",
|
|
164
|
+
},
|
|
150
165
|
offline: (name) => `${name} is not connected. Write it a message in the window where you opened ${name}, not in this page's chat: the page connects again by itself.`,
|
|
151
166
|
startClocks: "Start",
|
|
152
167
|
thinking: { move: "Thinking about its move", ponder: "Thinking while you play" },
|
|
@@ -190,6 +205,8 @@ const english = {
|
|
|
190
205
|
"profile-game": () => english.profile.whilePlaying,
|
|
191
206
|
"game-going": () => "A game is going on: finish it before you bring another to analyse.",
|
|
192
207
|
"pgn-unreadable": () => "Chesspal cannot read this PGN: check you copied the whole game, its tags and its moves.",
|
|
208
|
+
"update-game": () => english.update.whilePlaying,
|
|
209
|
+
"update-chat": (most, name) => `${name} is answering: update once it has done.`,
|
|
193
210
|
},
|
|
194
211
|
piece: (letter, side) => `${side} ${englishNames[letter]}`,
|
|
195
212
|
taken: (pieces, ahead) => `Pieces taken: ${pieces.length === 0 ? "none" : pieces.join(", ")}${ahead > 0 ? `; ahead by ${ahead}` : ""}`,
|
|
@@ -247,7 +264,6 @@ const english = {
|
|
|
247
264
|
back: "Back",
|
|
248
265
|
yourProfile: "Your profile",
|
|
249
266
|
editProfile: "Edit your profile",
|
|
250
|
-
yourPicture: "Your picture: edit your profile",
|
|
251
267
|
avatars: { "short-hair": "Short hair", bun: "Bun", glasses: "Glasses", beanie: "Beanie", beard: "Beard", curls: "Curls", moustache: "Moustache", "silver-bob": "Silver bob", cat: "Cat", owl: "Owl" },
|
|
252
268
|
whilePlaying: "Finish the game, or resign, before changing player.",
|
|
253
269
|
accounts: "Do you play on Lichess, Chess.com or in FIDE tournaments?",
|
|
@@ -352,25 +368,29 @@ const english = {
|
|
|
352
368
|
backward: ["Backward pawns", "A pawn left behind the pawns beside it, with the square in front of it attacked by an enemy pawn."],
|
|
353
369
|
passed: ["Passed pawns", "No enemy pawn in front of it, on its file or the files beside it."],
|
|
354
370
|
islands: ["Pawn islands", "Groups of pawns on neighbouring files, apart from each other."],
|
|
371
|
+
openFiles: ["Open files", "Files with no pawn of either side."],
|
|
355
372
|
halfOpen: ["Half-open files", "Files with enemy pawns and none of one's own."],
|
|
356
373
|
heavyOnFiles: ["Rooks and queens on open files", "Rooks and queens on open or half-open files."],
|
|
357
374
|
onSeventh: ["Rooks and queens on the seventh", "Rooks and queens on the enemy's second rank: the seventh for White, the second for Black."],
|
|
358
375
|
outposts: ["Outposts", "Squares on one's ranks 4 to 6, defended by a pawn, that no enemy pawn can ever attack."],
|
|
359
376
|
bishopPair: ["Bishop pair", "Both bishops still on the board."],
|
|
360
|
-
space: ["Space
|
|
377
|
+
space: ["Space", "How many squares of the enemy's half one's pieces attack."],
|
|
361
378
|
undeveloped: ["Minor pieces not developed", "Knights and bishops still on their first squares."],
|
|
362
379
|
castled: ["Castling", "How it castled; a dash while it still can, a cross once the king or the rooks have moved."],
|
|
363
380
|
kingAttacks: ["Enemy attacks around the king", "How many enemy attacks fall on the squares next to the king."],
|
|
364
381
|
},
|
|
365
|
-
groups: { pawns: "Pawns", pieces: "Pieces", king: "King" },
|
|
366
382
|
yes: "yes",
|
|
367
383
|
notYet: "not yet",
|
|
368
384
|
noMore: "no more",
|
|
369
385
|
phases: { opening: "Opening", middlegame: "Middlegame", endgame: "Endgame" },
|
|
370
|
-
openFiles: "Open files",
|
|
371
|
-
control: "Squares controlled",
|
|
372
|
-
both: "both",
|
|
373
386
|
controlled: (white, black, both) => `Squares only White's ${white}, only Black's ${black}, both ${both}`,
|
|
387
|
+
controlledBy: (who, count, you) => {
|
|
388
|
+
if (who === "both") {
|
|
389
|
+
return `Squares both control: ${count}`;
|
|
390
|
+
}
|
|
391
|
+
return `Squares only ${who === "white" ? "White" : "Black"}${you ? " (you)" : ""} controls: ${count}`;
|
|
392
|
+
},
|
|
393
|
+
bothSides: "both sides",
|
|
374
394
|
oppositeBishops: "Bishops of opposite colours",
|
|
375
395
|
repetitions: (times) => `This position has stood ${times} times`,
|
|
376
396
|
fiftyMoves: (halfMoves) => `${halfMoves} half-moves left before the fifty-move draw`,
|
|
@@ -394,9 +414,8 @@ const english = {
|
|
|
394
414
|
guide: {
|
|
395
415
|
title: "Guide",
|
|
396
416
|
contents: "Contents",
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
support: "Support Chesspal",
|
|
417
|
+
previous: "Previous page",
|
|
418
|
+
next: "Next page",
|
|
400
419
|
credits: "Credits",
|
|
401
420
|
creditLines: [
|
|
402
421
|
"The rules: [chess.js](https://github.com/jhlywa/chess.js), under the BSD 2-Clause license.",
|
|
@@ -405,6 +424,11 @@ const english = {
|
|
|
405
424
|
"The sounds: Chesspal's own.",
|
|
406
425
|
],
|
|
407
426
|
},
|
|
427
|
+
foot: {
|
|
428
|
+
onGitHub: "Chesspal on GitHub",
|
|
429
|
+
onNpm: "Chesspal on npm",
|
|
430
|
+
support: "Support Chesspal",
|
|
431
|
+
},
|
|
408
432
|
};
|
|
409
433
|
const italian = {
|
|
410
434
|
language: "it",
|
|
@@ -412,6 +436,7 @@ const italian = {
|
|
|
412
436
|
theme: "Tema chiaro o scuro",
|
|
413
437
|
sounds: "Suoni",
|
|
414
438
|
voice: "Leggi ad alta voce le parole dell'AI",
|
|
439
|
+
noVoice: "Questo browser non ha una voce per questa lingua: aggiungila nelle impostazioni del sistema",
|
|
415
440
|
saidMove: (move) => moveInWords(italianMoveWords, move),
|
|
416
441
|
newGame: "Nuova partita",
|
|
417
442
|
offerDraw: "Proponi patta",
|
|
@@ -530,6 +555,20 @@ const italian = {
|
|
|
530
555
|
boardLabel: (turn, lastMove) => `Scacchiera, ${italian.toMove(turn)}${lastMove === undefined ? "" : `, ultima mossa ${lastMove}`}`,
|
|
531
556
|
clearChat: (name) => `Prima di una nuova partita azzera la conversazione con ${name}: nella finestra dove hai aperto ${name}, non qui, scrivi /clear e poi un messaggio, per esempio "nuova partita". Con la chat pulita l'AI gioca più veloce.`,
|
|
532
557
|
outdated: (name) => `Chesspal è stato aggiornato, ma questa cartella ha ancora i file di prima: chiudi la chat con ${name}, esegui chesspal init in questa cartella e apri una chat nuova.`,
|
|
558
|
+
update: {
|
|
559
|
+
mark: (version) => `È uscito Chesspal ${version}: aggiorna`,
|
|
560
|
+
title: "Aggiorna Chesspal",
|
|
561
|
+
offer: (version, current) => `È uscita la versione ${version} di Chesspal${current === undefined ? "" : `; questa è la ${current}`}. Il tavolo la installa e riparte da solo.`,
|
|
562
|
+
chatAgain: (name) => `Anche la chat con ${name} riparte pulita.`,
|
|
563
|
+
byHand: (name) => `La chat con ${name} l'hai aperta tu: dopo, chiudila e aprine una nuova.`,
|
|
564
|
+
whilePlaying: "C'è una partita in corso: finiscila, poi aggiorna.",
|
|
565
|
+
install: "Aggiorna",
|
|
566
|
+
installing: (version) => `Installo la ${version}…`,
|
|
567
|
+
installed: "Installata: il tavolo riparte da solo a fine partita.",
|
|
568
|
+
failed: "Chesspal non si è aggiornato: chiudi la chat, poi scrivi in un terminale, uno dopo l'altro:",
|
|
569
|
+
notice: (version, name) => `È uscita la versione ${version} di Chesspal: chiudi la chat con ${name}, scrivi questo comando in un terminale e apri una chat nuova.`,
|
|
570
|
+
hide: "Nascondi fino alla prossima versione",
|
|
571
|
+
},
|
|
533
572
|
offline: (name) => `${name} è scollegato. Scrivigli un messaggio nella finestra dove hai aperto ${name}, non nella chat di questa pagina: la pagina si ricollega da sola.`,
|
|
534
573
|
startClocks: "Inizia",
|
|
535
574
|
thinking: { move: "Sta pensando alla sua mossa", ponder: "Pensa mentre giochi tu" },
|
|
@@ -573,6 +612,8 @@ const italian = {
|
|
|
573
612
|
"profile-game": () => italian.profile.whilePlaying,
|
|
574
613
|
"game-going": () => "C'è una partita in corso: finiscila prima di portarne un'altra da analizzare.",
|
|
575
614
|
"pgn-unreadable": () => "Chesspal non riesce a leggere questo PGN: controlla di aver copiato la partita intera, intestazioni e mosse.",
|
|
615
|
+
"update-game": () => italian.update.whilePlaying,
|
|
616
|
+
"update-chat": (most, name) => `${name} sta rispondendo: aggiorna quando ha finito.`,
|
|
576
617
|
},
|
|
577
618
|
// Queen and rook are feminine in Italian.
|
|
578
619
|
piece: (letter, side) => `${italianNames[letter]} ${side === "white" ? (letter === "Q" || letter === "R" ? "bianca" : "bianco") : (letter === "Q" || letter === "R" ? "nera" : "nero")}`,
|
|
@@ -631,7 +672,6 @@ const italian = {
|
|
|
631
672
|
back: "Indietro",
|
|
632
673
|
yourProfile: "Il tuo profilo",
|
|
633
674
|
editProfile: "Modifica il profilo",
|
|
634
|
-
yourPicture: "La tua immagine: modifica il profilo",
|
|
635
675
|
avatars: { "short-hair": "Capelli corti", bun: "Chignon", glasses: "Occhiali", beanie: "Berretto", beard: "Barba", curls: "Ricci", moustache: "Baffi", "silver-bob": "Caschetto grigio", cat: "Gatto", owl: "Gufo" },
|
|
636
676
|
whilePlaying: "Finisci la partita, o abbandona, prima di cambiare giocatore.",
|
|
637
677
|
accounts: "Giochi su Lichess, Chess.com o nei tornei FIDE?",
|
|
@@ -736,25 +776,29 @@ const italian = {
|
|
|
736
776
|
backward: ["Pedoni arretrati", "Un pedone rimasto indietro rispetto a quelli accanto, con la casa davanti attaccata da un pedone nemico."],
|
|
737
777
|
passed: ["Pedoni passati", "Nessun pedone nemico davanti a lui, né sulla sua colonna né su quelle accanto."],
|
|
738
778
|
islands: ["Isole di pedoni", "Gruppi di pedoni su colonne vicine, separati fra loro."],
|
|
779
|
+
openFiles: ["Colonne aperte", "Colonne senza pedoni di nessuno dei due."],
|
|
739
780
|
halfOpen: ["Colonne semiaperte", "Colonne con pedoni nemici e nessuno dei propri."],
|
|
740
781
|
heavyOnFiles: ["Torri e donna su colonne aperte", "Torri e donna su colonne aperte o semiaperte."],
|
|
741
782
|
onSeventh: ["Torri e donna in settima", "Torri e donna sulla seconda traversa del nemico: la settima per il Bianco, la seconda per il Nero."],
|
|
742
783
|
outposts: ["Avamposti", "Case sulle proprie traverse dalla quarta alla sesta, difese da un pedone, che nessun pedone nemico potrà mai attaccare."],
|
|
743
784
|
bishopPair: ["Coppia degli alfieri", "Tutti e due gli alfieri ancora sulla scacchiera."],
|
|
744
|
-
space: ["Spazio
|
|
785
|
+
space: ["Spazio", "Quante case della metà avversaria attaccano i propri pezzi."],
|
|
745
786
|
undeveloped: ["Pezzi minori non sviluppati", "Cavalli e alfieri ancora sulle case di partenza."],
|
|
746
787
|
castled: ["Arrocco", "Come ha arroccato; un trattino finché può ancora farlo, una croce quando il re o le torri si sono già mossi."],
|
|
747
788
|
kingAttacks: ["Attacchi nemici attorno al re", "Quanti attacchi nemici cadono sulle case accanto al re."],
|
|
748
789
|
},
|
|
749
|
-
groups: { pawns: "Pedoni", pieces: "Pezzi", king: "Re" },
|
|
750
790
|
yes: "sì",
|
|
751
791
|
notYet: "non ancora",
|
|
752
792
|
noMore: "non più",
|
|
753
793
|
phases: { opening: "Apertura", middlegame: "Mediogioco", endgame: "Finale" },
|
|
754
|
-
openFiles: "Colonne aperte",
|
|
755
|
-
control: "Case controllate",
|
|
756
|
-
both: "tutti e due",
|
|
757
794
|
controlled: (white, black, both) => `Case solo del Bianco ${white}, solo del Nero ${black}, di tutti e due ${both}`,
|
|
795
|
+
controlledBy: (who, count, you) => {
|
|
796
|
+
if (who === "both") {
|
|
797
|
+
return `Case controllate da tutti e due: ${count}`;
|
|
798
|
+
}
|
|
799
|
+
return `Case controllate solo dal ${who === "white" ? "Bianco" : "Nero"}${you ? " (tu)" : ""}: ${count}`;
|
|
800
|
+
},
|
|
801
|
+
bothSides: "tutti e due",
|
|
758
802
|
oppositeBishops: "Alfieri di colore contrario",
|
|
759
803
|
repetitions: (times) => `Questa posizione si è vista ${times} volte`,
|
|
760
804
|
fiftyMoves: (halfMoves) => `Mancano ${halfMoves} semimosse alla patta delle cinquanta mosse`,
|
|
@@ -778,9 +822,8 @@ const italian = {
|
|
|
778
822
|
guide: {
|
|
779
823
|
title: "Guida",
|
|
780
824
|
contents: "Indice",
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
support: "Sostieni Chesspal",
|
|
825
|
+
previous: "Pagina precedente",
|
|
826
|
+
next: "Pagina seguente",
|
|
784
827
|
credits: "Crediti",
|
|
785
828
|
creditLines: [
|
|
786
829
|
"Le regole: [chess.js](https://github.com/jhlywa/chess.js), con licenza BSD 2-Clause.",
|
|
@@ -789,6 +832,11 @@ const italian = {
|
|
|
789
832
|
"I suoni: di Chesspal.",
|
|
790
833
|
],
|
|
791
834
|
},
|
|
835
|
+
foot: {
|
|
836
|
+
onGitHub: "Chesspal su GitHub",
|
|
837
|
+
onNpm: "Chesspal su npm",
|
|
838
|
+
support: "Sostieni Chesspal",
|
|
839
|
+
},
|
|
792
840
|
};
|
|
793
841
|
// How each piece moves, said when it was taken where it cannot go (Anatolij's words).
|
|
794
842
|
const englishMoves = {
|
package/dist/page/theme.css
CHANGED
|
@@ -4,9 +4,9 @@ import { createHash } from "node:crypto";
|
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { get } from "node:http";
|
|
6
6
|
import { join, resolve } from "node:path";
|
|
7
|
-
import { writeAtomically } from "
|
|
8
|
-
import { dataFolder, tableFolder } from "
|
|
9
|
-
import { version } from "
|
|
7
|
+
import { writeAtomically } from "../../games/index.js";
|
|
8
|
+
import { dataFolder, tableFolder } from "../../home/index.js";
|
|
9
|
+
import { version } from "../../version.js";
|
|
10
10
|
/** What the chat hears from the doorbell: one line for whatever the player did, never what, only where to read it. */
|
|
11
11
|
export const bellLine = "The player acted on the board page: call status to see it.";
|
|
12
12
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Bell, bellProbe, listen, pageAt, pagePorts, samePath, type TableChat } from "./bell.ts";
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { closeSync, openSync, readdirSync, readFileSync, readSync, statSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { basename, dirname, join } from "node:path";
|
|
6
|
-
import { lastChatStart } from "
|
|
6
|
+
import { lastChatStart } from "../../games/index.js";
|
|
7
7
|
/** Who made the call with these metadata: the model and effort the host ties to it, else the host's name; undefined without a host. */
|
|
8
8
|
export async function callerOf(meta, client, folder, wait = 50) {
|
|
9
9
|
const turn = objectOf(meta?.["x-codex-turn-metadata"]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { startMcpServer } from "./mcp.ts";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { type AnyCommand, type Context } from "
|
|
2
|
+
import { type AnyCommand, type Context } from "../../commands/index.ts";
|
|
3
3
|
/**
|
|
4
4
|
* What the host reads as the server starts, before any tool: the lines that start a chat, `instructions` then `status`, and the rule of every move (D-27,
|
|
5
5
|
* D-102). Short, since hosts cut them or copy them into every tool.
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { aiMove, commands, defaultContext, inputProblems, isBoolean, PositionChanged } from "
|
|
7
|
-
import { aiNameOf } from "
|
|
8
|
-
import { endOf, loadGame, noteAiHost, noteAiModel, rerunIfChanged } from "
|
|
9
|
-
import { agentTools, hasAgent, startInstructions } from "
|
|
10
|
-
import { gameFolder } from "
|
|
11
|
-
import { version } from "
|
|
12
|
-
import { pagePorts } from "
|
|
6
|
+
import { aiMove, commands, defaultContext, inputProblems, isBoolean, PositionChanged } from "../../commands/index.js";
|
|
7
|
+
import { aiNameOf } from "../../core/index.js";
|
|
8
|
+
import { endOf, loadGame, noteAiHost, noteAiModel, rerunIfChanged } from "../../games/index.js";
|
|
9
|
+
import { agentTools, hasAgent, startInstructions } from "../../host-files/index.js";
|
|
10
|
+
import { gameFolder } from "../../profiles/index.js";
|
|
11
|
+
import { version } from "../../version.js";
|
|
12
|
+
import { pagePorts } from "../bell/index.js";
|
|
13
|
+
import { chatHasRead, startPage } from "../page/index.js";
|
|
14
|
+
import { attachTable, tableToJoin } from "../table/index.js";
|
|
13
15
|
import { allowedCaller, callerOf, callingAgent, fromCodexAgent } from "./caller.js";
|
|
14
|
-
import { chatHasRead, startPage } from "./page/page.js";
|
|
15
|
-
import { attachTable, tableToJoin } from "./table/index.js";
|
|
16
16
|
/**
|
|
17
17
|
* What the host reads as the server starts, before any tool: the lines that start a chat, `instructions` then `status`, and the rule of every move (D-27,
|
|
18
18
|
* D-102). Short, since hosts cut them or copy them into every tool.
|
package/dist/server/page/game.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { analyseGame, answerDraw, bookPlies, ChatNotClear, DrawRefused, endGame, gameAt, gameToStudy, namedGame, newGame, offerDraw, playMove, samePosition, say, shownPosition, startNewGame, } from "../../commands/index.js";
|
|
1
|
+
import { analyseGame, analystOf, answerDraw, bookPlies, ChatNotClear, DrawRefused, endGame, gameAt, gameToStudy, namedGame, newGame, offerDraw, playMove, samePosition, say, shownPosition, startNewGame, } from "../../commands/index.js";
|
|
2
2
|
import { clockStarted, isReviewed, writePgn } from "../../core/index.js";
|
|
3
3
|
import { aiModelOf, archiveName, coachShown, coachWords, drawOfferOf, endOf, gameInAnalysis, heardLines, loadGame, loadTableTalk, playerPgn, ponderingOn, rerunIfChanged, saveGame, startAnalysis, talkLimits, thoughtKey, wakesTheChat, WordsRefused, } from "../../games/index.js";
|
|
4
4
|
import { gameFolder, helpsIn } from "../../profiles/index.js";
|
|
5
|
+
import { version } from "../../version.js";
|
|
5
6
|
import { asTheChatSees } from "./chat.js";
|
|
6
7
|
import { bodyOf, bytesOf, isJson, json, jsonOf, runRoute, send, textOf } from "./http.js";
|
|
7
8
|
import { profilesShown } from "./profiles.js";
|
|
8
9
|
import { chatToRelaunch, relaunchChat, tableShown } from "./table.js";
|
|
10
|
+
import { updateShown } from "./update.js";
|
|
9
11
|
/** Answers the page's requests about the game: true when the request was one. */
|
|
10
12
|
export function answerGame(request, response, url, served) {
|
|
11
13
|
const route = routes[`${request.method ?? ""} ${url.pathname}`];
|
|
@@ -295,6 +297,8 @@ function sendGame(response, served) {
|
|
|
295
297
|
const analysis = current === undefined || endOf(current.game) !== undefined ? analysisShown(served) : undefined;
|
|
296
298
|
const aiModel = aiModelOf(folder);
|
|
297
299
|
const body = {
|
|
300
|
+
// The page loaded from another version loads again, its files and all (Federico).
|
|
301
|
+
version,
|
|
298
302
|
game,
|
|
299
303
|
...(game === null && talk.length > 0 ? { talk } : {}),
|
|
300
304
|
...(analysis === undefined ? {} : { analysis }),
|
|
@@ -303,6 +307,7 @@ function sendGame(response, served) {
|
|
|
303
307
|
listening: served.bell.listening,
|
|
304
308
|
...thinkingShown(served, current),
|
|
305
309
|
...(served.outdated ? { outdated: true } : {}),
|
|
310
|
+
...updateShown(served),
|
|
306
311
|
...tableShown(served),
|
|
307
312
|
...profilesShown(served.context.home()),
|
|
308
313
|
};
|
|
@@ -336,7 +341,10 @@ function coachOn(folder, current) {
|
|
|
336
341
|
const shown = coachShown(coachWords(folder, current), current);
|
|
337
342
|
return shown.judgments.length === 0 && shown.hint === undefined && shown.leads === undefined ? undefined : shown;
|
|
338
343
|
}
|
|
339
|
-
/**
|
|
344
|
+
/**
|
|
345
|
+
* The game in analysis, once no game goes on (D-69): its name and its PGN with the AI's review, and whether an AI is going over it now; none without one,
|
|
346
|
+
* or when it cannot be read.
|
|
347
|
+
*/
|
|
340
348
|
function analysisShown(served) {
|
|
341
349
|
const analysis = gameInAnalysis(gameFolder(served.context.home()));
|
|
342
350
|
if (analysis === undefined) {
|
|
@@ -344,8 +352,13 @@ function analysisShown(served) {
|
|
|
344
352
|
}
|
|
345
353
|
try {
|
|
346
354
|
const { name, record } = namedGame(served.context, analysis.game);
|
|
355
|
+
// Without a review, an AI is at it only while the chat is in a turn, or hears the player while its analyst holds the game (D-89): one the chat has
|
|
356
|
+
// started again since is gone (Priya). On the table the chat's start is in the chat's own folder.
|
|
357
|
+
const { bell } = served;
|
|
358
|
+
const chat = { ...served.context, folder: served.attached?.chat.folder ?? served.folder };
|
|
359
|
+
const reviewing = !isReviewed(record) && (bell.inTurn || (bell.listening && analystOf(chat, analysis.game) !== undefined));
|
|
347
360
|
// How far the book goes, which the page cannot tell without the book's moves: its card names the opening on those moves.
|
|
348
|
-
return { name, pgn: writePgn(record), book: bookPlies(record) };
|
|
361
|
+
return { name, pgn: writePgn(record), book: bookPlies(record), ...(reviewing ? { reviewing: true } : {}) };
|
|
349
362
|
}
|
|
350
363
|
catch {
|
|
351
364
|
// A game gone from the archive is no longer shown.
|
|
@@ -8,7 +8,7 @@ export declare const json = "application/json; charset=utf-8";
|
|
|
8
8
|
*/
|
|
9
9
|
export interface Refused {
|
|
10
10
|
readonly error: string;
|
|
11
|
-
readonly refusal?: "no-game" | "ai-to-move" | "illegal" | "clear-chat" | `words-${WordsRefused["reason"]}` | `draw-${DrawRefused["reason"]}` | "profile-game" | "game-going" | "pgn-unreadable";
|
|
11
|
+
readonly refusal?: "no-game" | "ai-to-move" | "illegal" | "clear-chat" | `words-${WordsRefused["reason"]}` | `draw-${DrawRefused["reason"]}` | "profile-game" | "game-going" | "pgn-unreadable" | "update-game" | "update-chat";
|
|
12
12
|
readonly most?: number;
|
|
13
13
|
}
|
|
14
14
|
/** Answers a request, with nothing cached, sniffed or referred, and the page's policy unless the headers give another. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ServerResponse } from "node:http";
|
|
2
2
|
import { type Context } from "../../commands/index.ts";
|
|
3
|
-
import { Bell } from "../bell.ts";
|
|
3
|
+
import { Bell } from "../bell/index.ts";
|
|
4
4
|
import { type Attached, type ChatSeen } from "./chat.ts";
|
|
5
5
|
import { type Opening, type TableChats } from "./table.ts";
|
|
6
6
|
/**
|
|
@@ -29,13 +29,19 @@ export interface PageOptions {
|
|
|
29
29
|
readonly table?: TableEnd;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
* What the page tells the table that serves it: a request came, so the table is in use, or another Chesspal version asked it to quit;
|
|
33
|
-
* opens a chat, which the page offers while none is at the table.
|
|
32
|
+
* What the page tells the table that serves it: a request came, so the table is in use, or another Chesspal version asked it to quit; how the table
|
|
33
|
+
* opens a chat, which the page offers while none is at the table, and how it updates Chesspal.
|
|
34
34
|
*/
|
|
35
35
|
export interface TableEnd {
|
|
36
36
|
used(): void;
|
|
37
37
|
quit(): void;
|
|
38
38
|
readonly chats?: TableChats | undefined;
|
|
39
|
+
readonly update?: TableUpdate | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** How the table updates Chesspal (D-108): npm installs a version, whether it did, and the table closes to start again from it. */
|
|
42
|
+
export interface TableUpdate {
|
|
43
|
+
install(version: string): Promise<boolean>;
|
|
44
|
+
restart(): void;
|
|
39
45
|
}
|
|
40
46
|
/**
|
|
41
47
|
* Serves the page of the context's folder on 127.0.0.1, on a port of its own when free, else on any free one, where the doorbell cannot find it; the
|
|
@@ -73,6 +79,9 @@ export interface Served {
|
|
|
73
79
|
restartWanted?: {
|
|
74
80
|
readonly host: string;
|
|
75
81
|
} | undefined;
|
|
82
|
+
/** The newer Chesspal npm has (D-108); the table installing it, installed and waiting to be at rest, or starting again from it. */
|
|
83
|
+
newer?: string;
|
|
84
|
+
updating?: "installing" | "installed" | "restarting" | undefined;
|
|
76
85
|
}
|
|
77
86
|
/**
|
|
78
87
|
* The chat has read the game as it is now, at the end of a tool's call: the doorbell wakes it only for what changes it after; and, when the tool `showed` it
|
package/dist/server/page/page.js
CHANGED
|
@@ -8,7 +8,7 @@ import { aiNameOf } from "../../core/index.js";
|
|
|
8
8
|
import { gameStamp, tableTalkStamp } from "../../games/index.js";
|
|
9
9
|
import { outdatedHostFiles } from "../../host-files/index.js";
|
|
10
10
|
import { gameFolder } from "../../profiles/index.js";
|
|
11
|
-
import { Bell, bellProbe, pagePorts } from "../bell.js";
|
|
11
|
+
import { Bell, bellProbe, pagePorts } from "../bell/index.js";
|
|
12
12
|
import { analysesOf, answerAnalyses } from "./analyses.js";
|
|
13
13
|
import { answerChat } from "./attach.js";
|
|
14
14
|
import { attachedSeen, chatSeenOf, pageContext } from "./chat.js";
|
|
@@ -17,7 +17,8 @@ import { sendFile, sendHtml } from "./files.js";
|
|
|
17
17
|
import { answerGame, chatCalls } from "./game.js";
|
|
18
18
|
import { json, send } from "./http.js";
|
|
19
19
|
import { answerProfiles, readRatingsOf } from "./profiles.js";
|
|
20
|
-
import { answerTable, restartWhenAtRest } from "./table.js";
|
|
20
|
+
import { answerTable, restartWhenAtRest, updateWhenAtRest } from "./table.js";
|
|
21
|
+
import { watchForUpdates } from "./update.js";
|
|
21
22
|
/**
|
|
22
23
|
* Serves the page of the context's folder on 127.0.0.1, on a port of its own when free, else on any free one, where the doorbell cannot find it; the
|
|
23
24
|
* table's on its own ports alone, which keep two tables apart. The page reads the chat from the context in the chat's process, and on the table from
|
|
@@ -33,9 +34,10 @@ export async function startPage(context, options = {}) {
|
|
|
33
34
|
context: pageContext(context),
|
|
34
35
|
chat,
|
|
35
36
|
opponent: options.opponent ?? (() => aiNameOf(chat.host())),
|
|
36
|
-
// A start again asked while the chat was at work comes once it has ended its answer (D-107).
|
|
37
|
+
// A start again asked while the chat was at work comes once it has ended its answer (D-107), as an update installed meanwhile (D-108).
|
|
37
38
|
bell: new Bell(() => hasNews(context, chat), () => {
|
|
38
39
|
restartWhenAtRest(served);
|
|
40
|
+
updateWhenAtRest(served);
|
|
39
41
|
tellWatchers(watchers);
|
|
40
42
|
}),
|
|
41
43
|
watchers,
|
|
@@ -49,7 +51,12 @@ export async function startPage(context, options = {}) {
|
|
|
49
51
|
if (await listened(server, port)) {
|
|
50
52
|
server.unref();
|
|
51
53
|
served.port = portOf(server);
|
|
52
|
-
const
|
|
54
|
+
const stopWatchingGame = watchGame(served);
|
|
55
|
+
const stopAskingNpm = watchForUpdates(served);
|
|
56
|
+
const stopWatching = () => {
|
|
57
|
+
stopWatchingGame();
|
|
58
|
+
stopAskingNpm();
|
|
59
|
+
};
|
|
53
60
|
return {
|
|
54
61
|
url: `http://127.0.0.1:${served.port}/`,
|
|
55
62
|
heard: () => tellWatchers(watchers),
|
|
@@ -188,6 +195,8 @@ function watchGame(served) {
|
|
|
188
195
|
waiting = undefined;
|
|
189
196
|
watchAll();
|
|
190
197
|
tellWatchers(served.watchers);
|
|
198
|
+
// A game that has ended lets an update installed meanwhile start the table again (D-108).
|
|
199
|
+
updateWhenAtRest(served);
|
|
191
200
|
}, 30);
|
|
192
201
|
}
|
|
193
202
|
};
|
|
@@ -16,7 +16,8 @@ export interface TableChats {
|
|
|
16
16
|
hosts(): readonly ChatHost[];
|
|
17
17
|
open(host: string): Promise<boolean>;
|
|
18
18
|
held(folder: string): HeldChat | undefined;
|
|
19
|
-
|
|
19
|
+
/** Asks for the chat again: with another host, or `updated`, from the Chesspal just installed (D-108). */
|
|
20
|
+
relaunch(folder: string, host?: string, updated?: boolean): boolean;
|
|
20
21
|
}
|
|
21
22
|
/** The chat `chesspal play` holds: the model and effort its host started with, none for the host's own. */
|
|
22
23
|
export interface HeldChat {
|
|
@@ -56,3 +57,8 @@ export declare function chatToRelaunch(served: Served): boolean;
|
|
|
56
57
|
export declare function relaunchChat(served: Served, how?: "again" | "restart", host?: string): boolean;
|
|
57
58
|
/** Starts the chat again as the page asked while it was at work, now that it has ended its answer; a chat gone meanwhile takes the request with it. */
|
|
58
59
|
export declare function restartWhenAtRest(served: Served): void;
|
|
60
|
+
/**
|
|
61
|
+
* Once npm has installed the newer Chesspal (D-108), and no game goes on and the chat is at rest, as a game or a chat may have started while npm worked:
|
|
62
|
+
* the chat `chesspal play` holds starts again from it, clean, and once it has left the table, the table closes to start again from it too.
|
|
63
|
+
*/
|
|
64
|
+
export declare function updateWhenAtRest(served: Served): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2
|
+
import { gameAt, samePosition } from "../../commands/index.js";
|
|
3
|
+
import { endOf, gameInAnalysis } from "../../games/index.js";
|
|
3
4
|
import { gameFolder } from "../../profiles/index.js";
|
|
4
5
|
import { tellWatchers } from "./events.js";
|
|
5
6
|
import { bodyOf, isJson, jsonOf, runRoute, sendJson } from "./http.js";
|
|
@@ -44,8 +45,13 @@ export function answerTable(request, response, url, served) {
|
|
|
44
45
|
else if (path === "/api/table/waiting") {
|
|
45
46
|
runRoute(response, () => endWaiting(request, response, served));
|
|
46
47
|
}
|
|
48
|
+
else if (path === "/api/table/update") {
|
|
49
|
+
runRoute(response, () => updateChesspal(request, response, served));
|
|
50
|
+
}
|
|
47
51
|
else {
|
|
48
|
-
sendJson(response, 404, {
|
|
52
|
+
sendJson(response, 404, {
|
|
53
|
+
error: `${path} is no route of the table's: it answers POST /api/table/chat, /api/table/restart, /api/table/waiting, /api/table/update and /api/table/quit.`,
|
|
54
|
+
});
|
|
49
55
|
}
|
|
50
56
|
return true;
|
|
51
57
|
}
|
|
@@ -160,6 +166,76 @@ async function endWaiting(request, response, served) {
|
|
|
160
166
|
sendJson(response, 200, now ? { restarting } : { waiting: false });
|
|
161
167
|
}
|
|
162
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Once npm has installed the newer Chesspal (D-108), and no game goes on and the chat is at rest, as a game or a chat may have started while npm worked:
|
|
171
|
+
* the chat `chesspal play` holds starts again from it, clean, and once it has left the table, the table closes to start again from it too.
|
|
172
|
+
*/
|
|
173
|
+
export function updateWhenAtRest(served) {
|
|
174
|
+
if (served.updating !== "installed" || gameGoingOn(served) || !tableAtRest(served)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
served.updating = "restarting";
|
|
178
|
+
tellWatchers(served.watchers);
|
|
179
|
+
const relaunched = served.table?.chats?.relaunch(served.folder, undefined, true) === true;
|
|
180
|
+
void restartOnceLeft(served, relaunched);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Installs the newer Chesspal npm has (D-108), asked by the table's own page alone, never while a game goes on, the chat is at work or one is opening;
|
|
184
|
+
* the table then starts again from it as soon as it is at rest. Whether npm installed it; when it did not, the page gives the command to type.
|
|
185
|
+
*/
|
|
186
|
+
async function updateChesspal(request, response, served) {
|
|
187
|
+
await bodyOf(request);
|
|
188
|
+
const { newer, table } = served;
|
|
189
|
+
if (!fromTablePage(request, served)) {
|
|
190
|
+
sendJson(response, 403, { error: "Only the table's own page updates Chesspal." });
|
|
191
|
+
}
|
|
192
|
+
else if (newer === undefined || table?.update === undefined) {
|
|
193
|
+
sendJson(response, 409, { error: "No newer Chesspal to install: this one is the latest npm has." });
|
|
194
|
+
}
|
|
195
|
+
else if (served.updating !== undefined) {
|
|
196
|
+
sendJson(response, 409, { error: "Chesspal is being updated already: wait for the table to start again." });
|
|
197
|
+
}
|
|
198
|
+
else if (gameGoingOn(served)) {
|
|
199
|
+
sendJson(response, 409, { error: "A game is going on: finish it, then update Chesspal.", refusal: "update-game" });
|
|
200
|
+
}
|
|
201
|
+
else if (!tableAtRest(served)) {
|
|
202
|
+
sendJson(response, 409, { error: "The chat is at work, or opening: update Chesspal once it is at rest.", refusal: "update-chat" });
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
served.updating = "installing";
|
|
206
|
+
tellWatchers(served.watchers);
|
|
207
|
+
let installed = false;
|
|
208
|
+
try {
|
|
209
|
+
installed = await table.update.install(newer);
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
served.updating = installed ? "installed" : undefined;
|
|
213
|
+
}
|
|
214
|
+
// Starting again ends every connection: once this answer has gone, or the page that asked has.
|
|
215
|
+
response.once("close", () => updateWhenAtRest(served));
|
|
216
|
+
tellWatchers(served.watchers);
|
|
217
|
+
sendJson(response, 200, { installed });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// The chat started again leaves the table as `chesspal play` ends its host: a table closed before would leave its server to serve its own page on the
|
|
221
|
+
// table's ports, where the new table would then find them taken (Priya). One that does not leave in time is left to it.
|
|
222
|
+
async function restartOnceLeft(served, relaunched) {
|
|
223
|
+
for (let tries = 0; relaunched && served.attached !== undefined && tries < leaveTries; tries++) {
|
|
224
|
+
await delay(100);
|
|
225
|
+
}
|
|
226
|
+
served.table?.update?.restart();
|
|
227
|
+
}
|
|
228
|
+
// `chesspal play` sees the request within half a second and ends its host in a few more.
|
|
229
|
+
const leaveTries = 100;
|
|
230
|
+
// A game with moves still to come, a new one too: never an update meanwhile (D-108).
|
|
231
|
+
function gameGoingOn(served) {
|
|
232
|
+
const current = gameAt(gameFolder(served.context.home()), served.context.now());
|
|
233
|
+
return current !== undefined && endOf(current.game) === undefined;
|
|
234
|
+
}
|
|
235
|
+
// No chat is at work at the table, and none is opening.
|
|
236
|
+
function tableAtRest(served) {
|
|
237
|
+
return (served.attached === undefined || chatAtRest(served)) && opening(served) === undefined;
|
|
238
|
+
}
|
|
163
239
|
// The browser marks a request of the table's own page with its origin.
|
|
164
240
|
function fromTablePage(request, served) {
|
|
165
241
|
return request.headers.origin === `http://127.0.0.1:${served.port}` || request.headers.origin === `http://localhost:${served.port}`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Served } from "./page.ts";
|
|
2
|
+
/**
|
|
3
|
+
* What the page shows of a newer Chesspal: its version, and on the table whether it is being installed, or installed and waiting for the game to end and
|
|
4
|
+
* the chat to be at rest.
|
|
5
|
+
*/
|
|
6
|
+
export interface UpdateShown {
|
|
7
|
+
readonly version: string;
|
|
8
|
+
readonly installing?: true;
|
|
9
|
+
readonly installed?: true;
|
|
10
|
+
}
|
|
11
|
+
/** Asks npm for Chesspal's latest version now and once a day after, the pages told when it is newer than this one; how to stop. None in the tests. */
|
|
12
|
+
export declare function watchForUpdates(served: Served, every?: number): () => void;
|
|
13
|
+
/** The newer Chesspal the page shows, when npm has one. */
|
|
14
|
+
export declare function updateShown(served: Served): {
|
|
15
|
+
update?: UpdateShown;
|
|
16
|
+
};
|
|
17
|
+
/** Chesspal's latest version on npm, none when npm cannot be reached in time or answers something else. */
|
|
18
|
+
export declare function latestVersion(fetch: typeof globalThis.fetch): Promise<string | undefined>;
|