@hajimism/dek 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/package.json +2 -1
- package/src/cli/env-file.ts +78 -0
- package/src/cli.ts +5 -1
- package/src/core/github.ts +27 -24
- package/src/core/playwright-module.d.ts +3 -2
- package/src/core/playwright-visual.ts +3 -1
- package/src/core/playwright.ts +14 -1
- package/src/core/slide-script-eval.ts +2 -1
- package/src/core/spawn.ts +8 -1
- package/src/core/sync.ts +20 -1
- package/src/video/worker.ts +2 -0
- package/tsconfig.json +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
Release notes for `@hajimism/dek`. From 0.1.0 on, release-please writes this file from the Conventional Commits on `main`.
|
|
4
4
|
|
|
5
|
+
## [0.3.0](https://github.com/hajimism/dek/compare/v0.2.2...v0.3.0) (2026-10-07)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### ⚠ BREAKING CHANGES
|
|
9
|
+
|
|
10
|
+
* a DEK_ variable kept in a project's .env, such as DEK_VOICE_URL, no longer applies; set it in the shell, or use `engine` in voice.toml.
|
|
11
|
+
* reading a private ref needs GITHUB_TOKEN or GH_TOKEN; being signed in to gh is not enough, and DEK_GH is gone. `GITHUB_TOKEN=$(gh auth token) dekc ref ...` does what dek did before. No token is sent to DEK_GITHUB_API.
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* draw pages in Chromium's sandbox with the network off ([550cc3a](https://github.com/hajimism/dek/commit/550cc3ab1c44005d4ef89420d3bb92a44a7da3a9))
|
|
16
|
+
* ignore what the .env.*.local files set, and NODE_TLS_REJECT_UNAUTHORIZED from any .env ([da02b7c](https://github.com/hajimism/dek/commit/da02b7c4375b41ddd9ea2d879714950e5017cbc2))
|
|
17
|
+
* keep a ref's title to one line of AGENTS.md ([c1b0b60](https://github.com/hajimism/dek/commit/c1b0b609a8710985c32988cff19b8cb65a0e6bc6))
|
|
18
|
+
* run dekc, and every bun it starts, without the working directory's bunfig.toml ([0418909](https://github.com/hajimism/dek/commit/04189098b6975ba37506f039e865388205c5817b))
|
|
19
|
+
* ship tsconfig.json, so a project's paths cannot take over dek's imports ([3eed8f0](https://github.com/hajimism/dek/commit/3eed8f0961a232a48be1cb06cd08dc03e121d026))
|
|
20
|
+
* stop asking gh for a token, and send one only to api.github.com ([62c896b](https://github.com/hajimism/dek/commit/62c896ba1b69424e5426534040a087a2d42bef1a))
|
|
21
|
+
* take DEK_ variables only from the environment, never from a .env ([7f4629b](https://github.com/hajimism/dek/commit/7f4629b3099e23a4c46b6261fa95627659fbfe51))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Documentation
|
|
25
|
+
|
|
26
|
+
* **adr:** number the GHSA-mj6q-67q7-q923 decision after the ones on main ([4a888b0](https://github.com/hajimism/dek/commit/4a888b06594c188bf0efa88fb9111f48f70263e9))
|
|
27
|
+
* **adr:** take GitHub tokens and DEK_ variables only from what the user sets ([c07fa4c](https://github.com/hajimism/dek/commit/c07fa4ceb7e1df69fb184ee8955f1819ee58a069))
|
|
28
|
+
* record the ref title and shot fixes in ADR 12 and the architecture guide ([a6bfb93](https://github.com/hajimism/dek/commit/a6bfb9383c9c614c5c39f0e2f6817db24dc83e06))
|
|
29
|
+
* say what a project cannot set for the Bun that runs dek, and what stays out of scope ([632efbb](https://github.com/hajimism/dek/commit/632efbbe4697a2175bf9cd0c8b5db77e53dbfa17))
|
|
30
|
+
|
|
5
31
|
## [0.2.2](https://github.com/hajimism/dek/compare/v0.2.1...v0.2.2) (2026-10-06)
|
|
6
32
|
|
|
7
33
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hajimism/dek",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A build system for talks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Hajime Nakagawa (https://github.com/hajimism)",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"src",
|
|
13
13
|
"skills",
|
|
14
|
+
"tsconfig.json",
|
|
14
15
|
"LICENSE",
|
|
15
16
|
"README.md",
|
|
16
17
|
"README.ja.md",
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { ansi, shouldColor, terminalSafe } from "./tty.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The files Bun may read into process.env from the working directory before dek starts: `.env`,
|
|
7
|
+
* `.env.local`, and the two for NODE_ENV, plain and `.local`. All of them are read whatever
|
|
8
|
+
* NODE_ENV is, so a change in which ones Bun picks cannot let a variable through.
|
|
9
|
+
*/
|
|
10
|
+
const ENV_FILES = [
|
|
11
|
+
".env",
|
|
12
|
+
".env.local",
|
|
13
|
+
...["development", "production", "test"].flatMap((mode) => [
|
|
14
|
+
`.env.${mode}`,
|
|
15
|
+
`.env.${mode}.local`,
|
|
16
|
+
]),
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Every name dek takes only from the environment that a file holds, however the line around it is
|
|
21
|
+
* written: `K=v`, `export K=v`, `K: v`. Besides DEK_ variables, NODE_TLS_REJECT_UNAUTHORIZED: Bun
|
|
22
|
+
* reads it on each request, and with it a proxy that a `.env` named could read the token `dekc ref`
|
|
23
|
+
* sends. Other variables Bun acts on, such as HTTPS_PROXY, take effect before dek starts, and
|
|
24
|
+
* deleting them changes nothing.
|
|
25
|
+
*/
|
|
26
|
+
const NAME_RE = /DEK_[A-Z0-9_]*|NODE_TLS_REJECT_UNAUTHORIZED/gi;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Takes out of `env` every variable in NAME_RE that a `.env` file in `cwd` may have set, and
|
|
30
|
+
* returns their names. Bun loads those files before dek runs, so a repository someone else wrote
|
|
31
|
+
* could otherwise choose the programs dek runs and the host `dekc ref` calls. These variables come
|
|
32
|
+
* only from the environment dek was started in. Under `--no-env-file`, which the dekc bin passes,
|
|
33
|
+
* Bun loaded nothing, so every one there is the user's own.
|
|
34
|
+
*/
|
|
35
|
+
export function dropEnvFileVariables(
|
|
36
|
+
cwd: string,
|
|
37
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
38
|
+
execArgv: readonly string[] = process.execArgv,
|
|
39
|
+
): string[] {
|
|
40
|
+
if (execArgv.includes("--no-env-file")) {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
const names = new Set<string>();
|
|
44
|
+
for (const file of ENV_FILES) {
|
|
45
|
+
let text: string;
|
|
46
|
+
try {
|
|
47
|
+
text = readFileSync(join(cwd, file), "utf8");
|
|
48
|
+
} catch {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (const [name] of text.matchAll(NAME_RE)) {
|
|
52
|
+
names.add(name.toUpperCase());
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const dropped = [...names].filter((name) => env[name] !== undefined).sort();
|
|
56
|
+
for (const name of dropped) {
|
|
57
|
+
delete env[name];
|
|
58
|
+
}
|
|
59
|
+
return dropped;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** What dek says on stderr about the variables `dropEnvFileVariables` took out. */
|
|
63
|
+
export function envFileWarning(names: readonly string[], color = false): string {
|
|
64
|
+
const c = ansi(color);
|
|
65
|
+
const list = names.join(", ");
|
|
66
|
+
return [
|
|
67
|
+
`${c.yellow("warning:")} ignored ${list}: a .env file in this directory sets it, and dek takes it only from the environment`,
|
|
68
|
+
` ${c.yellow("help:")} set it in the shell that runs dekc instead`,
|
|
69
|
+
].join("\n");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The bin's first step: drop what a `.env` set, and say so. */
|
|
73
|
+
export function ignoreEnvFileVariables(cwd: string): void {
|
|
74
|
+
const dropped = dropEnvFileVariables(cwd);
|
|
75
|
+
if (dropped.length > 0) {
|
|
76
|
+
process.stderr.write(`${terminalSafe(envFileWarning(dropped, shouldColor(process.stderr)))}\n`);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
1
|
+
#!/usr/bin/env -S bun --no-env-file --config=/dev/null
|
|
2
|
+
import { ignoreEnvFileVariables } from "./cli/env-file.ts";
|
|
2
3
|
import { main } from "./cli/main.ts";
|
|
3
4
|
|
|
5
|
+
// `bun ./node_modules/.bin/dekc` skips the line above and loads `.env` anyway. It reads
|
|
6
|
+
// `bunfig.toml` too, and nothing here can undo a preload that has already run.
|
|
7
|
+
ignoreEnvFileVariables(process.cwd());
|
|
4
8
|
await main(Bun.argv.slice(2), process.cwd());
|
package/src/core/github.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { DekError } from "./error.ts";
|
|
|
3
3
|
/**
|
|
4
4
|
* The two GitHub calls dekc ref needs: pin a rev to a commit, and download
|
|
5
5
|
* that commit. DEK_GITHUB_API points them elsewhere, the way DEK_VOICE_URL
|
|
6
|
-
* does for the voice engine.
|
|
6
|
+
* does for the voice engine, but a token only ever goes here.
|
|
7
7
|
*/
|
|
8
8
|
const DEFAULT_API = "https://api.github.com";
|
|
9
9
|
|
|
@@ -16,9 +16,6 @@ const COMMIT_TIMEOUT_MS = 30 * 1000;
|
|
|
16
16
|
/** A tarball near the size cap on a slow line still fits well inside this. */
|
|
17
17
|
const TARBALL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
18
18
|
|
|
19
|
-
const SIGN_IN_HINT =
|
|
20
|
-
"if the repository is private, set GITHUB_TOKEN or run `gh auth login`, then run the command again";
|
|
21
|
-
|
|
22
19
|
export type GithubOptions = {
|
|
23
20
|
/** Covers the whole call, from connecting to the last byte of the body. */
|
|
24
21
|
timeoutMs?: number;
|
|
@@ -32,22 +29,27 @@ function apiBase(): string {
|
|
|
32
29
|
return (process.env.DEK_GITHUB_API ?? DEFAULT_API).replace(/\/+$/, "");
|
|
33
30
|
}
|
|
34
31
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const printed = result.exitCode === 0 ? result.stdout.toString().trim() : "";
|
|
47
|
-
return printed || undefined;
|
|
48
|
-
} catch {
|
|
32
|
+
/**
|
|
33
|
+
* The Authorization header for a call to `base`: the token the user set in GITHUB_TOKEN, else
|
|
34
|
+
* GH_TOKEN, and only for api.github.com. Public repositories need none. dek never asks `gh` for
|
|
35
|
+
* one: that token can write to every repository its owner can, and a tool that takes it unasked
|
|
36
|
+
* reads as credential theft to the machine's security software.
|
|
37
|
+
*/
|
|
38
|
+
export function authorization(
|
|
39
|
+
base: string,
|
|
40
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
41
|
+
): string | undefined {
|
|
42
|
+
if (base !== DEFAULT_API) {
|
|
49
43
|
return undefined;
|
|
50
44
|
}
|
|
45
|
+
const token = env.GITHUB_TOKEN?.trim() || env.GH_TOKEN?.trim();
|
|
46
|
+
return token ? `Bearer ${token}` : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function signInHint(base: string): string {
|
|
50
|
+
return base === DEFAULT_API
|
|
51
|
+
? "if the repository is private, set GITHUB_TOKEN to a token that can read it (`gh auth token` prints yours), then run the command again"
|
|
52
|
+
: "DEK_GITHUB_API points dek away from api.github.com, and a token goes only there; unset it to read a private repository";
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
/**
|
|
@@ -78,7 +80,7 @@ function deadline(what: string, timeoutMs: number): Deadline {
|
|
|
78
80
|
|
|
79
81
|
function rateLimitHint(retryAfter: string | null): string {
|
|
80
82
|
const wait = retryAfter ? `wait ${retryAfter}s, and ` : "";
|
|
81
|
-
return `${wait}set GITHUB_TOKEN
|
|
83
|
+
return `${wait}set GITHUB_TOKEN to raise the limit, then run the command again`;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
async function request(
|
|
@@ -87,14 +89,15 @@ async function request(
|
|
|
87
89
|
accept: string,
|
|
88
90
|
{ signal, timeout, expired }: Deadline,
|
|
89
91
|
): Promise<Response> {
|
|
90
|
-
const
|
|
92
|
+
const base = apiBase();
|
|
93
|
+
const auth = authorization(base);
|
|
91
94
|
let response: Response;
|
|
92
95
|
try {
|
|
93
|
-
response = await fetch(`${
|
|
96
|
+
response = await fetch(`${base}${path}`, {
|
|
94
97
|
headers: {
|
|
95
98
|
accept,
|
|
96
99
|
"user-agent": "dek",
|
|
97
|
-
...(auth ? { authorization:
|
|
100
|
+
...(auth ? { authorization: auth } : {}),
|
|
98
101
|
},
|
|
99
102
|
redirect: "follow",
|
|
100
103
|
signal,
|
|
@@ -112,7 +115,7 @@ async function request(
|
|
|
112
115
|
return response;
|
|
113
116
|
}
|
|
114
117
|
if (response.status === 404) {
|
|
115
|
-
throw new DekError(`${what} not found on GitHub`, { hint:
|
|
118
|
+
throw new DekError(`${what} not found on GitHub`, { hint: signInHint(base) });
|
|
116
119
|
}
|
|
117
120
|
const retryAfter = response.headers.get("retry-after");
|
|
118
121
|
if (
|
|
@@ -125,7 +128,7 @@ async function request(
|
|
|
125
128
|
}
|
|
126
129
|
if (response.status === 401 || response.status === 403) {
|
|
127
130
|
throw new DekError(`GitHub refused access to ${what} (${response.status})`, {
|
|
128
|
-
hint:
|
|
131
|
+
hint: signInHint(base),
|
|
129
132
|
});
|
|
130
133
|
}
|
|
131
134
|
throw new DekError(`GitHub answered ${response.status} for ${what}`, {
|
|
@@ -33,14 +33,15 @@ declare module "playwright" {
|
|
|
33
33
|
close(): Promise<void>;
|
|
34
34
|
};
|
|
35
35
|
export type Browser = {
|
|
36
|
-
newPage(options?: { viewport?: Viewport }): Promise<Page>;
|
|
36
|
+
newPage(options?: { viewport?: Viewport; offline?: boolean }): Promise<Page>;
|
|
37
37
|
newContext(options?: {
|
|
38
38
|
viewport?: Viewport;
|
|
39
39
|
deviceScaleFactor?: number;
|
|
40
|
+
offline?: boolean;
|
|
40
41
|
}): Promise<BrowserContext>;
|
|
41
42
|
close(): Promise<void>;
|
|
42
43
|
};
|
|
43
44
|
export const chromium: {
|
|
44
|
-
launch(options?: { headless?: boolean }): Promise<Browser>;
|
|
45
|
+
launch(options?: { headless?: boolean; chromiumSandbox?: boolean }): Promise<Browser>;
|
|
45
46
|
};
|
|
46
47
|
}
|
|
@@ -50,9 +50,11 @@ export async function runVisualRequest<R extends VisualRequest>(
|
|
|
50
50
|
browser: Browser,
|
|
51
51
|
request: R,
|
|
52
52
|
): Promise<ResponseTo<R>> {
|
|
53
|
-
// A PPTX's pictures are shown full screen, so they are drawn at twice the slide's size.
|
|
53
|
+
// A PPTX's pictures are shown full screen, so they are drawn at twice the slide's size. A deck is
|
|
54
|
+
// self-contained (DEK020), so the page needs no network, and a ref's slide scripts get none.
|
|
54
55
|
const context = await browser.newContext({
|
|
55
56
|
viewport: request.viewport,
|
|
57
|
+
offline: true,
|
|
56
58
|
...(request.kind === "pptx" ? { deviceScaleFactor: 2 } : {}),
|
|
57
59
|
});
|
|
58
60
|
try {
|
package/src/core/playwright.ts
CHANGED
|
@@ -260,7 +260,7 @@ export async function runPlaywrightWorker<Request, Response>(
|
|
|
260
260
|
}
|
|
261
261
|
const request = await readWorkerRequest<Request>();
|
|
262
262
|
try {
|
|
263
|
-
const browser = await playwright.chromium
|
|
263
|
+
const browser = await launchChromium(playwright.chromium);
|
|
264
264
|
try {
|
|
265
265
|
const response = await handle(browser, request);
|
|
266
266
|
process.stdout.write(`${JSON.stringify(response)}\n`);
|
|
@@ -272,6 +272,19 @@ export async function runPlaywrightWorker<Request, Response>(
|
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Headless Chromium with its sandbox, which Playwright leaves off by default: slide scripts run in
|
|
277
|
+
* it, a ref's included. Where the sandbox cannot start, as in a container running as root, it
|
|
278
|
+
* runs without one rather than not at all.
|
|
279
|
+
*/
|
|
280
|
+
async function launchChromium(chromium: typeof import("playwright").chromium): Promise<Browser> {
|
|
281
|
+
try {
|
|
282
|
+
return await chromium.launch({ headless: true, chromiumSandbox: true });
|
|
283
|
+
} catch {
|
|
284
|
+
return await chromium.launch({ headless: true });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
275
288
|
export async function defaultPlaywrightRunner(
|
|
276
289
|
request: VisualRequest,
|
|
277
290
|
options: SpawnTimeoutOptions = {},
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type SlideScriptProblem,
|
|
8
8
|
staticProblems,
|
|
9
9
|
} from "./slide-script.ts";
|
|
10
|
+
import { BUN_FLAGS } from "./spawn.ts";
|
|
10
11
|
import { suggest } from "./suggest.ts";
|
|
11
12
|
|
|
12
13
|
/** One slide script as lint checks it: its source, and the slide's step keys to check `motion` by. */
|
|
@@ -149,7 +150,7 @@ const EVALUATOR_OPTIONS = { stdout: "pipe", stderr: "ignore", env: {} } as const
|
|
|
149
150
|
|
|
150
151
|
function evaluatorCommand(): string[] {
|
|
151
152
|
const worker = moduleFilePath(new URL("./slide-eval-worker.ts", import.meta.url));
|
|
152
|
-
return [process.execPath,
|
|
153
|
+
return [process.execPath, ...BUN_FLAGS, worker];
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
/** A worker that was killed still wrote a line for every script it finished. */
|
package/src/core/spawn.ts
CHANGED
|
@@ -13,9 +13,16 @@ export async function awaitPiped(proc: {
|
|
|
13
13
|
return { stdout, stderr, exitCode };
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* How dek runs bun: with no auto install, and without the working directory's `.env` or
|
|
18
|
+
* `bunfig.toml`, which a repository someone else wrote can commit to set variables or preload code.
|
|
19
|
+
* The dekc bin's shebang passes the last two to dek itself; a child does not inherit them.
|
|
20
|
+
*/
|
|
21
|
+
export const BUN_FLAGS = ["--no-install", "--no-env-file", "--config=/dev/null"];
|
|
22
|
+
|
|
16
23
|
/** `bin` as a command line: a .ts worker runs under bun, anything else as is. */
|
|
17
24
|
export function workerCommand(bin: string, args: string[] = []): string[] {
|
|
18
|
-
return bin.endsWith(".ts") ? ["bun",
|
|
25
|
+
return bin.endsWith(".ts") ? ["bun", ...BUN_FLAGS, bin, ...args] : [bin, ...args];
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
/** A browser that hangs must not hang dek; a whole visual run or capture fits well inside this. */
|
package/src/core/sync.ts
CHANGED
|
@@ -293,6 +293,25 @@ For commands, run \`dekc help --agent\`.
|
|
|
293
293
|
`;
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
/** The longest ref title AGENTS.md shows: a title names a deck, it does not describe one. */
|
|
297
|
+
const REF_TITLE_MAX = 80;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* A ref's title as it goes into AGENTS.md. Someone else wrote it, and agents read dek's block as
|
|
301
|
+
* dek's own guidance, so a title must not start a line of its own there, such as a heading or an
|
|
302
|
+
* instruction: line breaks and control characters become one space, and it is cut to
|
|
303
|
+
* REF_TITLE_MAX characters.
|
|
304
|
+
*/
|
|
305
|
+
function refTitleLine(title: string | undefined): string | undefined {
|
|
306
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching them is the point.
|
|
307
|
+
const line = title?.replace(/[\s\x00-\x1f\x7f-\x9f]+/gu, " ").trim();
|
|
308
|
+
if (!line) {
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
const chars = [...line];
|
|
312
|
+
return chars.length > REF_TITLE_MAX ? `${chars.slice(0, REF_TITLE_MAX - 1).join("")}…` : line;
|
|
313
|
+
}
|
|
314
|
+
|
|
296
315
|
/**
|
|
297
316
|
* The refs dek.toml pins, one line each, so an agent knows which decks it may
|
|
298
317
|
* read as models. Empty when there are none, leaving AGENTS.md as before. A
|
|
@@ -311,7 +330,7 @@ function referencesSection(root: string): string {
|
|
|
311
330
|
return "";
|
|
312
331
|
}
|
|
313
332
|
const lines = refs.map((name) => {
|
|
314
|
-
const title = refTitle(refDir(root, name), parseRefSource(name).deck)?.title;
|
|
333
|
+
const title = refTitleLine(refTitle(refDir(root, name), parseRefSource(name).deck)?.title);
|
|
315
334
|
return title ? `- \`${name}\`: ${title}` : `- \`${name}\``;
|
|
316
335
|
});
|
|
317
336
|
return `
|
package/src/video/worker.ts
CHANGED
|
@@ -21,8 +21,10 @@ async function capture(
|
|
|
21
21
|
request: VideoCaptureRequest,
|
|
22
22
|
): Promise<VideoCaptureResponse> {
|
|
23
23
|
mkdirSync(request.outDir, { recursive: true });
|
|
24
|
+
// A deck is self-contained (DEK020), so the page needs no network.
|
|
24
25
|
const page = await browser.newPage({
|
|
25
26
|
viewport: { width: request.viewport.width, height: request.viewport.height },
|
|
27
|
+
offline: true,
|
|
26
28
|
});
|
|
27
29
|
await loadVideoDoc(page, request.html);
|
|
28
30
|
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["ESNext"],
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"module": "Preserve",
|
|
6
|
+
"moduleDetection": "force",
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"types": ["bun"],
|
|
9
|
+
|
|
10
|
+
"moduleResolution": "bundler",
|
|
11
|
+
"allowImportingTsExtensions": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
|
|
15
|
+
"strict": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"noFallthroughCasesInSwitch": true,
|
|
18
|
+
"noUncheckedIndexedAccess": true,
|
|
19
|
+
"noImplicitOverride": true,
|
|
20
|
+
|
|
21
|
+
"noUnusedLocals": false,
|
|
22
|
+
"noUnusedParameters": false,
|
|
23
|
+
"noPropertyAccessFromIndexSignature": false
|
|
24
|
+
},
|
|
25
|
+
"include": ["src", "tests"]
|
|
26
|
+
}
|