chesspal 0.3.1 → 0.3.3
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} +4 -4
- package/dist/commands/{agents.js → answers/agents.js} +13 -9
- 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 +53 -21
- package/dist/page/app/served.js +15 -3
- 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 +26 -3
- 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 +201 -88
- package/dist/page/helps/helps.js +50 -31
- 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/table/window.js +6 -5
- package/dist/page/talk/chat.css +6 -6
- package/dist/page/talk/talk.css +17 -67
- package/dist/page/talk/talk.js +6 -6
- package/dist/page/talk/voice.js +12 -0
- package/dist/page/text.js +78 -20
- package/dist/page/theme.css +2 -0
- package/dist/server/{bell.js → bell/bell.js} +17 -4
- 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/attach.js +8 -3
- package/dist/server/page/game.js +19 -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 +26 -5
- 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
|
@@ -4,9 +4,10 @@ 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 {
|
|
7
|
+
import { writeAtomically } from "../../games/index.js";
|
|
8
|
+
import { dataFolder, tableFolder } from "../../home/index.js";
|
|
9
|
+
import { analystAgent } from "../../host-files/index.js";
|
|
10
|
+
import { version } from "../../version.js";
|
|
10
11
|
/** What the chat hears from the doorbell: one line for whatever the player did, never what, only where to read it. */
|
|
11
12
|
export const bellLine = "The player acted on the board page: call status to see it.";
|
|
12
13
|
/**
|
|
@@ -171,7 +172,8 @@ export async function bellHeard(input, ports) {
|
|
|
171
172
|
const pages = ours.length > 1 ? `${ours.length} pages of it, none of this chat's session, ${open} open in a browser` : "no page of it";
|
|
172
173
|
return { folder, rang: false, why: `${pages} at ports ${tried.join(", ")}${others.length > 0 ? `; ${others.join(", ")}` : ""}` };
|
|
173
174
|
}
|
|
174
|
-
|
|
175
|
+
// The page lets the analyst go as the hook starts waiting, when none runs any more.
|
|
176
|
+
const said = await body(port, hook.analystGone === true ? "/api/bell?analyst=gone" : "/api/bell");
|
|
175
177
|
return said === "ring" ? { folder, rang: true } : { folder, rang: false, why: said === undefined ? `the page at port ${port} did not answer` : `the page at port ${port} said "${said}"` };
|
|
176
178
|
}
|
|
177
179
|
// How long a port may take to say whose page it is: one of this computer answers at once.
|
|
@@ -244,12 +246,23 @@ function hookInput(input) {
|
|
|
244
246
|
return {
|
|
245
247
|
...(typeof parsed.session_id === "string" ? { session: parsed.session_id } : {}),
|
|
246
248
|
...(typeof parsed.cwd === "string" ? { cwd: parsed.cwd } : {}),
|
|
249
|
+
...(analystGone(parsed.background_tasks) ? { analystGone: true } : {}),
|
|
247
250
|
};
|
|
248
251
|
}
|
|
249
252
|
catch {
|
|
250
253
|
return {};
|
|
251
254
|
}
|
|
252
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Whether Claude Code's Stop hook says no analyst runs in the background: after it ends, is stopped or fails, the chat takes a turn, and its Stop hook
|
|
258
|
+
* lists the tasks still running (D-109, Priya's test). A host that lists none says nothing.
|
|
259
|
+
*/
|
|
260
|
+
function analystGone(tasks) {
|
|
261
|
+
return Array.isArray(tasks) && !tasks.some((task) => {
|
|
262
|
+
const { agent_type: agent, status } = (task ?? {});
|
|
263
|
+
return agent === analystAgent && status === "running";
|
|
264
|
+
});
|
|
265
|
+
}
|
|
253
266
|
/**
|
|
254
267
|
* The chat's page among those of its folder: the one of its session; else the only one, or the only one a browser has open, where the player plays. Codex
|
|
255
268
|
* gives the page no session, and a chat closed may leave its page behind for a while.
|
|
@@ -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.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The table's end of the chat attached to it (D-104): the chat attaches and stays while its process lives, says when it calls a tool, sends a copy of what
|
|
2
2
|
// it saw, and asks for the New game window. One chat at a time, of this version alone; a page a chat serves itself has none of these routes.
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { forgetTheAi, noteAiHost, noteAiModel } from "../../games/index.js";
|
|
4
|
+
import { forgetAnalyst, forgetTheAi, noteAiHost, noteAiModel } from "../../games/index.js";
|
|
5
5
|
import { gameFolder } from "../../profiles/index.js";
|
|
6
6
|
import { version } from "../../version.js";
|
|
7
7
|
import { chatAttach, chatCopy } from "./chat.js";
|
|
@@ -67,10 +67,15 @@ function attach(body, response, served) {
|
|
|
67
67
|
});
|
|
68
68
|
tellWatchers(served.watchers);
|
|
69
69
|
}
|
|
70
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* The next chat may play in another host, with another model: the AI's card shows its own from its first move (Federico). The analyst ended with its chat
|
|
72
|
+
* (D-109): the game it held is free for the next one.
|
|
73
|
+
*/
|
|
71
74
|
function forgetChat(served) {
|
|
72
75
|
try {
|
|
73
|
-
|
|
76
|
+
const folder = gameFolder(served.context.home());
|
|
77
|
+
forgetTheAi(folder);
|
|
78
|
+
forgetAnalyst(folder);
|
|
74
79
|
}
|
|
75
80
|
catch {
|
|
76
81
|
// A data folder that cannot be read keeps no notes to forget.
|
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,16 @@ 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 reviewed = isReviewed(record);
|
|
360
|
+
const reviewing = !reviewed && (bell.inTurn || (bell.listening && analystOf(chat, analysis.game) !== undefined));
|
|
361
|
+
// Without a review and with nobody at it, the analysis is incomplete: the page offers to bring it back to the AI (D-109).
|
|
362
|
+
const state = reviewed ? {} : reviewing ? { reviewing: true } : { incomplete: true };
|
|
347
363
|
// 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) };
|
|
364
|
+
return { name, pgn: writePgn(record), book: bookPlies(record), ...state };
|
|
349
365
|
}
|
|
350
366
|
catch {
|
|
351
367
|
// 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
|
@@ -5,10 +5,10 @@ import { createServer } from "node:http";
|
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { positionIn, samePosition } from "../../commands/index.js";
|
|
7
7
|
import { aiNameOf } from "../../core/index.js";
|
|
8
|
-
import { gameStamp, tableTalkStamp } from "../../games/index.js";
|
|
8
|
+
import { forgetAnalyst, 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),
|
|
@@ -134,6 +141,9 @@ function answer(request, response, served) {
|
|
|
134
141
|
send(response, 200, json, JSON.stringify(bellProbe(served.folder, served.watchers.size > 0, tableChat(served))));
|
|
135
142
|
}
|
|
136
143
|
else {
|
|
144
|
+
if (url.searchParams.get("analyst") === "gone") {
|
|
145
|
+
forgetGoneAnalyst(served);
|
|
146
|
+
}
|
|
137
147
|
served.bell.listen(request, response);
|
|
138
148
|
}
|
|
139
149
|
}
|
|
@@ -141,6 +151,15 @@ function answer(request, response, served) {
|
|
|
141
151
|
sendFile(response, path);
|
|
142
152
|
}
|
|
143
153
|
}
|
|
154
|
+
/** The chat's hook says no analyst runs any more (D-109): the game it held is free, and its review, unless finished, incomplete. */
|
|
155
|
+
function forgetGoneAnalyst(served) {
|
|
156
|
+
try {
|
|
157
|
+
forgetAnalyst(gameFolder(served.context.home()));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// A data folder that cannot be written keeps the analyst until half an hour has gone by.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
144
163
|
/** The chat at the table, as its probe says it: none on a page a chat serves itself. */
|
|
145
164
|
function tableChat(served) {
|
|
146
165
|
if (served.table === undefined) {
|
|
@@ -188,6 +207,8 @@ function watchGame(served) {
|
|
|
188
207
|
waiting = undefined;
|
|
189
208
|
watchAll();
|
|
190
209
|
tellWatchers(served.watchers);
|
|
210
|
+
// A game that has ended lets an update installed meanwhile start the table again (D-108).
|
|
211
|
+
updateWhenAtRest(served);
|
|
191
212
|
}, 30);
|
|
192
213
|
}
|
|
193
214
|
};
|
|
@@ -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>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// A newer Chesspal on npm (D-108): the page's server asks as it starts and once a day after; the table's page offers to install it, a chat's own page says
|
|
2
|
+
// the command to type.
|
|
3
|
+
import { answerTime, askSite } from "../../sites/index.js";
|
|
4
|
+
import { isOlder, version } from "../../version.js";
|
|
5
|
+
import { tellWatchers } from "./events.js";
|
|
6
|
+
/** 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. */
|
|
7
|
+
export function watchForUpdates(served, every = aDay) {
|
|
8
|
+
const fetch = served.context.fetch;
|
|
9
|
+
if (fetch === undefined) {
|
|
10
|
+
return () => undefined;
|
|
11
|
+
}
|
|
12
|
+
const ask = async () => {
|
|
13
|
+
const latest = await latestVersion(fetch);
|
|
14
|
+
if (latest !== undefined && isOlder(version, latest) && latest !== served.newer) {
|
|
15
|
+
served.newer = latest;
|
|
16
|
+
tellWatchers(served.watchers);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
void ask();
|
|
20
|
+
const timer = setInterval(() => void ask(), every);
|
|
21
|
+
timer.unref();
|
|
22
|
+
return () => clearInterval(timer);
|
|
23
|
+
}
|
|
24
|
+
/** The newer Chesspal the page shows, when npm has one. */
|
|
25
|
+
export function updateShown(served) {
|
|
26
|
+
const { newer, updating } = served;
|
|
27
|
+
if (newer === undefined) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
return { update: { version: newer, ...(updating === "installed" ? { installed: true } : updating === undefined ? {} : { installing: true }) } };
|
|
31
|
+
}
|
|
32
|
+
/** Chesspal's latest version on npm, none when npm cannot be reached in time or answers something else. */
|
|
33
|
+
export async function latestVersion(fetch) {
|
|
34
|
+
const answer = await askSite(latestUrl, fetch, AbortSignal.timeout(answerTime));
|
|
35
|
+
const said = answer?.body?.version;
|
|
36
|
+
// Only a plain release: the version goes into npm's command line.
|
|
37
|
+
return answer?.status === 200 && typeof said === "string" && /^\d+\.\d+\.\d+$/.test(said) ? said : undefined;
|
|
38
|
+
}
|
|
39
|
+
const latestUrl = "https://registry.npmjs.org/chesspal/latest";
|
|
40
|
+
const aDay = 24 * 60 * 60 * 1000;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Opens an address in a window of its own, else in a tab of the default browser, without waiting for it: whether a browser started. */
|
|
2
2
|
export declare function openInBrowser(url: string, platform: string): Promise<boolean>;
|
|
3
|
-
/** The address of the window the table opens, marked so that the page sizes it
|
|
3
|
+
/** The address of the window the table opens, marked so that the page sizes it as it opens (page/layout/window.ts). */
|
|
4
4
|
export declare function openedAddress(url: string): string;
|
|
5
5
|
/** Chrome or Edge, where each system installs them: the first there is; none when neither is. */
|
|
6
6
|
export declare function appBrowser(platform: string, env: NodeJS.ProcessEnv, exists?: (path: string) => boolean): string | undefined;
|
|
@@ -10,7 +10,7 @@ export async function openInBrowser(url, platform) {
|
|
|
10
10
|
// Explorer, open and xdg-open take an address as they take a folder, and hand it to the default browser.
|
|
11
11
|
return (browser !== undefined && await started(browser, [`--app=${openedAddress(url)}`])) || openFolder(url, platform);
|
|
12
12
|
}
|
|
13
|
-
/** The address of the window the table opens, marked so that the page sizes it
|
|
13
|
+
/** The address of the window the table opens, marked so that the page sizes it as it opens (page/layout/window.ts). */
|
|
14
14
|
export function openedAddress(url) {
|
|
15
15
|
const address = new URL(url);
|
|
16
16
|
address.searchParams.set("opened", "");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from "../../../commands/index.ts";
|
|
2
|
-
import type { TableChats } from "../../page/
|
|
2
|
+
import type { TableChats } from "../../page/index.ts";
|
|
3
3
|
/**
|
|
4
4
|
* The chats of a real table: the hosts installed, looked for again once a while has passed, since the player may install one with the page open; a
|
|
5
5
|
* terminal opened with `chesspal play`, run by `chesspal`, the same Node and script as the table's.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { tableFolder } from "../../../home/index.js";
|
|
2
|
-
import { samePath } from "../../bell.js";
|
|
2
|
+
import { samePath } from "../../bell/index.js";
|
|
3
3
|
import { programPath } from "./find.js";
|
|
4
4
|
import { hostChoices, hostNames, isHostName } from "./hosts.js";
|
|
5
5
|
import { askAgain, seatHeld } from "./seat.js";
|
|
@@ -24,7 +24,7 @@ export function terminalChats(context, chesspal = [process.execPath, ...process.
|
|
|
24
24
|
const seat = samePath(folder, tableFolder(context.home())) ? seatHeld(folder, context.now()) : undefined;
|
|
25
25
|
return seat === undefined ? undefined : { model: seat.model, effort: seat.effort };
|
|
26
26
|
},
|
|
27
|
-
relaunch: (folder, host) => samePath(folder, tableFolder(context.home())) && askAgain(folder, context.now(), host === undefined ? {} : { host }),
|
|
27
|
+
relaunch: (folder, host, updated) => samePath(folder, tableFolder(context.home())) && askAgain(folder, context.now(), { ...(host === undefined ? {} : { host }), ...(updated === true ? { updated } : {}) }),
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
// Looking on the PATH takes a few milliseconds, and the page asks at every change of the game.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// The AI hosts a table opens a chat in (D-104): their choices, the terminal they open in, and `chesspal play`, which starts one there.
|
|
2
2
|
export { terminalChats } from "./chats.js";
|
|
3
3
|
export { isHostName } from "./hosts.js";
|
|
4
|
-
export { hostLaunch, playHost } from "./play.js";
|
|
4
|
+
export { endTree, hostLaunch, playHost } from "./play.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
1
2
|
import type { Context } from "../../../commands/index.ts";
|
|
2
3
|
import { type HostChoice, type HostName } from "./hosts.ts";
|
|
3
4
|
/**
|
|
@@ -14,7 +15,8 @@ export interface HostLaunch {
|
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* Runs the host in the table's folder, in this terminal, until it ends: its exit code; started again, clean, when the table asks for a new game (D-57),
|
|
17
|
-
* or for another model or host, which then takes its place (D-107). Ctrl+C is the host's: this
|
|
18
|
+
* or for another model or host, which then takes its place (D-107); after an update, by the Chesspal installed (D-108). Ctrl+C is the host's: this
|
|
19
|
+
* process ignores it, or the terminal would close with the chat.
|
|
18
20
|
*/
|
|
19
21
|
export declare function playHost(context: Context, host: HostName): Promise<number>;
|
|
20
22
|
/**
|
|
@@ -27,3 +29,8 @@ export declare function runHost(launch: HostLaunch, again: () => boolean, platfo
|
|
|
27
29
|
}>;
|
|
28
30
|
/** How the host starts for the profile in use: found on the PATH, with its choice; it throws, saying how to install it, when it is not there. */
|
|
29
31
|
export declare function hostLaunch(context: Context, host: HostName, env?: NodeJS.ProcessEnv, exists?: (path: string) => boolean): HostLaunch;
|
|
32
|
+
/**
|
|
33
|
+
* Ends a process and what it started, a host's MCP server among them: on Windows its whole tree, which taskkill ends, never the console (Priya); else the
|
|
34
|
+
* process, which ends its own. A taskkill that failed leaves it to a kill.
|
|
35
|
+
*/
|
|
36
|
+
export declare function endTree(chat: ChildProcess, platform: string): void;
|