@litfamily/litopencode 1.0.37 โ 1.0.38
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 +10 -0
- package/README-Ko-KR.md +6 -6
- package/README.md +6 -6
- package/dist/state.d.ts +1 -0
- package/dist/state.js +1 -0
- package/dist/tools.d.ts +15 -5
- package/dist/tui/mission-band.d.ts +7 -0
- package/dist/tui/mission-band.js +4 -0
- package/dist/tui/mission-text.d.ts +3 -3
- package/dist/tui/mission-text.js +5 -5
- package/dist/v2/events.d.ts +1 -1
- package/dist/v2/events.js +31 -5
- package/dist/v2/notices.d.ts +13 -0
- package/dist/v2/notices.js +143 -0
- package/dist/v2/tui-footer.d.ts +20 -0
- package/dist/v2/tui-footer.js +56 -0
- package/dist/v2/tui.d.ts +6 -0
- package/dist/v2/tui.js +47 -12
- package/dist/v2/usage-limits.d.ts +19 -0
- package/dist/v2/usage-limits.js +70 -0
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/reference-Ko-KR.md +14 -3
- package/docs/reference.md +16 -4
- package/package.json +6 -2
- package/skills/lit-typographic-motion/requirements-audio.txt +92 -89
- package/skills/managed-skill-manifest.json +4 -4
- package/skills/readme-studio/templates/hyperframes/package-lock.json +4 -4
- package/skills/readme-studio/templates/remotion/package-lock.json +3 -3
- package/skills/readme-studio/templates/remotion/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ are tracked separately from this product history.
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 1.0.38 - 2026-11-02
|
|
9
|
+
|
|
10
|
+
- Dependency alerts cleared: zod 4.6.6 (the runtime dependency and the Remotion cover template), the audio runtime's msgpack 1.2.3, @hono/node-server 2.1.5 in the HyperFrames cover template, and toml 5.0.1 in the development tree.
|
|
11
|
+
- Releases are faster: the publish workflow no longer repeats the test suite and the QA probes. It checks with GitHub that CI already passed on the exact commit being released and stops if it did not. A publish from your own machine still runs the full suite first.
|
|
12
|
+
- On OpenCode 2 the footer line beside the prompt now steps down with the terminal width instead of being clipped by the host: the ignition mark with the whole stage chip, the mark with a smaller chip, the chip alone, `๐ฅ 1/3`, the bare flame, then nothing. Before, a narrow terminal cut the mark mid-word and mangled the host's own figures next to it.
|
|
13
|
+
- On OpenCode 2 the status card shows the OpenCode Go plan windows (`5h 42% ยท 1w 18%`) for sessions on the `opencode-go` provider, read from the host's usage source and asked at most once a minute. Other providers and signed-out hosts show nothing, because OpenCode 2 gives a plugin no other usage data.
|
|
14
|
+
- The automatic-handoff notices now reach the screen on OpenCode 2. The server plugin cannot raise a toast there, so it leaves each notice in `.litopencode/notices.jsonl` and the TUI plugin shows it.
|
|
15
|
+
- Automatic handoff now fires on OpenCode 2. The plugin waited for a `session.status` or `session.idle` event that OpenCode 2 never publishes, so a turn that crossed the percent was never followed by the handoff request. It now reads the end of a turn from `session.execution.succeeded`, `failed` and `interrupted` (and the start from `session.execution.started`), which also records the session as idle for the status card.
|
|
16
|
+
- A flaky test of the alias goal path now waits for the ledger event instead of only the goal in the fake host, so slow runners no longer fail it.
|
|
17
|
+
|
|
8
18
|
## 1.0.37 - 2026-11-01
|
|
9
19
|
|
|
10
20
|
- `lit-typographic-motion` films can now carry real keyed footage, such as a reaction clip or a spinning-cat loop shot on a green screen. Key it outside the renderer into 8-bit RGBA PNG frames, put them in `stage/clips/<folder>/` and list them in `stage/clips/clips.json` (`id`, `dir`, `count`, `fps`). The page loads `/lit/clips.js`, registers a clip once with `LitStage.clip({ parent, id, box })` and shows it from `render(t)`; the frame is picked from film time, so a cue is plain arithmetic and the determinism replay holds. A declared clip is checked against its bytes (12 clips, 360 frames each and 1500 in all, 40 MB each and 128 MB in all, 720 px on the long side, up to 30 fps, real transparency) and is exempt from the flipbook rule; any frame sequence you did not declare is still refused with exit 17. A cover takes no clips. The flash audit and the text checks run on the final pixels, and the report lists each clip. `LitStage.clip.circle`, `wipe` and `iris` keep working.
|
package/README-Ko-KR.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion.webp" width="100%" alt="LitFamily ๋ชจ์
์ปค๋ฒ: ์ผ๊ณฑ ๋ก๋ด ํจ๋์ด ์ฐจ๋ก๋ก ์ผ์ง๊ณ , LitOpenCode ๋ก๋ด์ ๋๊ณผ ํ
๋๋ฆฌ๊ฐ ๋น๋ ๋ค LITFAMILY์ KEEP THE WORK LIT. ๋ฌธ๊ตฌ๊ฐ ๋ฐ์์ง๋ ์์" /></picture></p>
|
|
2
2
|
|
|
3
3
|
# LitOpenCode
|
|
4
4
|
|
|
5
5
|
**LitOpenCode๋ OpenCode์ฉ ์ํฌํ๋ก ํ๋ฌ๊ทธ์ธ์
๋๋ค. ํ๋กฌํํธ์ `lit`๋ง ๋ถ์ด๋ฉด ๊ณํํ๊ณ , ๋ง๋ค๊ณ , ํ์ธํ๊ณ , ๊ธฐ๋ก๊น์ง ๋จ๊ฒจ์ ๋ค์ ์ธ์
์ด ์ด๋์ ๋ฉ์ท๋์ง ๋ฐ๋ก ์๋๋ค.**
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
8
|
+
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-version.svg" alt="1.0.38" /></a>
|
|
9
9
|
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="์๊ฐ npm ๋ค์ด๋ก๋ ์" /></a>
|
|
10
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
10
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-license.svg" alt="MIT ๋ผ์ด์ ์ค" /></a>
|
|
11
11
|
<a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub ์คํ" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
|
|
|
57
57
|
## ๋ ๋ณด๊ธฐ
|
|
58
58
|
|
|
59
59
|
- [GitHub์ ์ฌ์ฉ ์๋ด](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide-Ko-KR.md): ์ค์น ์ต์
, ์๋ ์ธ์์ธ๊ณ, ๋ฌธ์ ํด๊ฒฐ, ์ ๊ฑฐ
|
|
60
|
-
- [์ํฌํ๋ก ์์ธ ์๋ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
61
|
-
- [๋ง์ด๊ทธ๋ ์ด์
์๋ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
62
|
-
- [LitFamily ํ๋ธ](https://github.com/wjgoarxiv/litfamily) ยท [MIT ๋ผ์ด์ ์ค](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
60
|
+
- [์ํฌํ๋ก ์์ธ ์๋ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/reference-Ko-KR.md) ยท [English reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/reference.md)
|
|
61
|
+
- [๋ง์ด๊ทธ๋ ์ด์
์๋ด](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/migration.md) ยท [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/CHANGELOG.md) ยท [๊ฐ์ธ์ ๋ณด์ ๋คํธ์ํฌ ๋์](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/privacy.md)
|
|
62
|
+
- [LitFamily ํ๋ธ](https://github.com/wjgoarxiv/litfamily) ยท [MIT ๋ผ์ด์ ์ค](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE) ยท [์ํ ์ ์ฑ
](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/TRADEMARK.md) ยท [ASCII ๊ธ๊ผด ๋ผ์ด์ ์ค](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/JetBrainsMono-OFL.txt)
|
|
63
63
|
|
|
64
64
|
<details>
|
|
65
65
|
<summary>ASCII ๋ก๊ณ ๋ณต์ฌ</summary>
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
1
|
+
<p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: seven armored robots power on one by one, the LitOpenCode robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
|
|
2
2
|
|
|
3
3
|
# LitOpenCode
|
|
4
4
|
|
|
5
5
|
**LitOpenCode is a workflow plugin for OpenCode. Add `lit` to a prompt and the work gets planned, built, checked and written down, so the next session knows where you stopped.**
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
8
|
+
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-version.svg" alt="1.0.38" /></a>
|
|
9
9
|
<a href="https://www.npmjs.com/package/@litfamily/litopencode"><img src="https://img.shields.io/npm/dm/@litfamily/litopencode?label=downloads&color=D7F75B" alt="npm downloads per month" /></a>
|
|
10
|
-
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
10
|
+
<a href="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
|
|
11
11
|
<a href="https://github.com/wjgoarxiv/litopencode"><img src="https://img.shields.io/github/stars/wjgoarxiv/litopencode?style=social" alt="GitHub stars" /></a>
|
|
12
12
|
</p>
|
|
13
13
|
|
|
@@ -57,9 +57,9 @@ npm uninstall -g @litfamily/litopencode
|
|
|
57
57
|
## More
|
|
58
58
|
|
|
59
59
|
- [Guide on GitHub](https://github.com/wjgoarxiv/litopencode/blob/main/docs/guide.md): install options, automatic handoff, troubleshooting and removal.
|
|
60
|
-
- [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
61
|
-
- [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
62
|
-
- [LitFamily hub](https://github.com/wjgoarxiv/litfamily) ยท [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.
|
|
60
|
+
- [Workflow reference](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/reference.md)
|
|
61
|
+
- [Migration notes](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/migration.md) ยท [Changelog](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/CHANGELOG.md) ยท [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/privacy.md)
|
|
62
|
+
- [LitFamily hub](https://github.com/wjgoarxiv/litfamily) ยท [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/LICENSE) ยท [Trademark policy](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/TRADEMARK.md) ยท [ASCII font license](https://cdn.jsdelivr.net/npm/@litfamily/litopencode@1.0.38/docs/assets/readme/JetBrainsMono-OFL.txt)
|
|
63
63
|
|
|
64
64
|
<details>
|
|
65
65
|
<summary>Copy ASCII logo</summary>
|
package/dist/state.d.ts
CHANGED
package/dist/state.js
CHANGED
|
@@ -34,6 +34,7 @@ export function createRuntimePaths(projectRoot) {
|
|
|
34
34
|
knowledgeDir: path.join(runtimeDir, "knowledge"),
|
|
35
35
|
knowledgeClaimsFile: path.join(runtimeDir, "knowledge", "claims.jsonl"),
|
|
36
36
|
autoHandoffFile: path.join(runtimeDir, "auto-handoff.json"),
|
|
37
|
+
noticesFile: path.join(runtimeDir, "notices.jsonl"),
|
|
37
38
|
workflowStackDir: path.join(runtimeDir, "workflow-stack"),
|
|
38
39
|
adhdDir: path.join(runtimeDir, "adhd"),
|
|
39
40
|
livenessDir: path.join(runtimeDir, "liveness"),
|
package/dist/tools.d.ts
CHANGED
|
@@ -10,34 +10,44 @@ export declare const litOpenCodeTools: Readonly<{
|
|
|
10
10
|
args: Readonly<{
|
|
11
11
|
[k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
|
|
12
12
|
}>;
|
|
13
|
-
execute(args:
|
|
13
|
+
execute(args: {
|
|
14
|
+
[x: string]: unknown;
|
|
15
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
|
|
14
16
|
};
|
|
15
17
|
litwork: {
|
|
16
18
|
description: string;
|
|
17
19
|
args: Readonly<{
|
|
18
20
|
[k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
|
|
19
21
|
}>;
|
|
20
|
-
execute(args:
|
|
22
|
+
execute(args: {
|
|
23
|
+
[x: string]: unknown;
|
|
24
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
|
|
21
25
|
};
|
|
22
26
|
"start-work": {
|
|
23
27
|
description: string;
|
|
24
28
|
args: Readonly<{
|
|
25
29
|
[k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
|
|
26
30
|
}>;
|
|
27
|
-
execute(args:
|
|
31
|
+
execute(args: {
|
|
32
|
+
[x: string]: unknown;
|
|
33
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
|
|
28
34
|
};
|
|
29
35
|
"review-work": {
|
|
30
36
|
description: string;
|
|
31
37
|
args: Readonly<{
|
|
32
38
|
[k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
|
|
33
39
|
}>;
|
|
34
|
-
execute(args:
|
|
40
|
+
execute(args: {
|
|
41
|
+
[x: string]: unknown;
|
|
42
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
|
|
35
43
|
};
|
|
36
44
|
wikify: {
|
|
37
45
|
description: string;
|
|
38
46
|
args: Readonly<{
|
|
39
47
|
[k: string]: import("zod/v4/core").$ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
|
|
40
48
|
}>;
|
|
41
|
-
execute(args:
|
|
49
|
+
execute(args: {
|
|
50
|
+
[x: string]: unknown;
|
|
51
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<ToolResult>;
|
|
42
52
|
};
|
|
43
53
|
}>;
|
|
@@ -36,6 +36,13 @@ export type CardFigures = {
|
|
|
36
36
|
readonly branch?: string;
|
|
37
37
|
readonly inLane: boolean;
|
|
38
38
|
readonly restartTo?: string;
|
|
39
|
+
readonly limits?: readonly CardLimit[];
|
|
40
|
+
};
|
|
41
|
+
/** A plan window the host reports (`5h 42%`), shown after the context figure; absent when the host reports none. */
|
|
42
|
+
export type CardLimit = {
|
|
43
|
+
readonly label: string;
|
|
44
|
+
readonly percent: number;
|
|
45
|
+
readonly limited: boolean;
|
|
39
46
|
};
|
|
40
47
|
export type CardInputs = {
|
|
41
48
|
readonly view: MissionView;
|
package/dist/tui/mission-band.js
CHANGED
|
@@ -150,6 +150,7 @@ function topLine(view, frame, palette, width, border, rest, mark) {
|
|
|
150
150
|
}
|
|
151
151
|
return fitLine([{ text: "โญ" + "โ".repeat(Math.max(0, width)), fg: border }], width, palette);
|
|
152
152
|
}
|
|
153
|
+
const limitWarnPercent = 90;
|
|
153
154
|
/** The figures in the bottom border, most important first; the last ones go first when room runs out. */
|
|
154
155
|
function figureParts(view, figures, palette) {
|
|
155
156
|
const parts = [];
|
|
@@ -159,6 +160,9 @@ function figureParts(view, figures, palette) {
|
|
|
159
160
|
const percent = Math.round(figures.contextPercent);
|
|
160
161
|
parts.push({ text: `ctx ${percent}%`, fg: percent >= contextWarnPercent ? palette.warning : palette.muted });
|
|
161
162
|
}
|
|
163
|
+
for (const limit of figures.limits ?? []) {
|
|
164
|
+
parts.push({ text: `${limit.label} ${limit.percent}%${limit.limited ? " limited" : ""}`, fg: limit.limited || limit.percent >= limitWarnPercent ? palette.warning : palette.muted });
|
|
165
|
+
}
|
|
162
166
|
if (view.active)
|
|
163
167
|
parts.push({ text: `โณ ${elapsedText(view.elapsedMinutes)}${figures.inLane ? " ยท in lane" : ""}`, fg: palette.muted });
|
|
164
168
|
if (figures.branch !== undefined)
|
|
@@ -100,13 +100,13 @@ export declare function groupsWidth(groups: readonly ChipGroup[]): number;
|
|
|
100
100
|
export declare function fade(segment: Segment, alpha: number, palette: MissionPalette): Segment;
|
|
101
101
|
export declare const chipSparkWidth = 7;
|
|
102
102
|
export declare function chipSparkCells(t: number, palette: MissionPalette): Segment[] | undefined;
|
|
103
|
-
export declare function chipTier(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number): ChipTier;
|
|
103
|
+
export declare function chipTier(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number, largest?: ChipTier): ChipTier;
|
|
104
104
|
/**
|
|
105
105
|
* The map chip on the right of the prompt's agent/model row: `๐ฅ LIT plan โ โบ WORK โฐโฐโฐโฑโฑโฑโฑ 3/7 โบ review`,
|
|
106
106
|
* then the live step when the full chip leaves room for it, shedding labels before values as `budget`
|
|
107
|
-
* shrinks
|
|
107
|
+
* shrinks, and never larger than `largest`. Idle sessions render nothing.
|
|
108
108
|
*/
|
|
109
|
-
export declare function chipLine(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number): Line;
|
|
109
|
+
export declare function chipLine(view: MissionView, frame: MotionFrame, palette: MissionPalette, budget: number, largest?: ChipTier): Line;
|
|
110
110
|
/** The header status: a glyph and words, or the COMPLETE badge that pops in and settles to green text. */
|
|
111
111
|
export declare function statusSegments(view: MissionView, frame: MotionFrame, palette: MissionPalette): {
|
|
112
112
|
readonly segments: Segment[];
|
package/dist/tui/mission-text.js
CHANGED
|
@@ -396,8 +396,8 @@ export function chipSparkCells(t, palette) {
|
|
|
396
396
|
}
|
|
397
397
|
return cells;
|
|
398
398
|
}
|
|
399
|
-
export function chipTier(view, frame, palette, budget) {
|
|
400
|
-
for (const tier of ["full", "medium"]) {
|
|
399
|
+
export function chipTier(view, frame, palette, budget, largest = "full") {
|
|
400
|
+
for (const tier of largest === "full" ? ["full", "medium"] : largest === "medium" ? ["medium"] : []) {
|
|
401
401
|
if (groupsWidth(chipGroups(view, frame, palette, tier)) <= budget)
|
|
402
402
|
return tier;
|
|
403
403
|
}
|
|
@@ -406,12 +406,12 @@ export function chipTier(view, frame, palette, budget) {
|
|
|
406
406
|
/**
|
|
407
407
|
* The map chip on the right of the prompt's agent/model row: `๐ฅ LIT plan โ โบ WORK โฐโฐโฐโฑโฑโฑโฑ 3/7 โบ review`,
|
|
408
408
|
* then the live step when the full chip leaves room for it, shedding labels before values as `budget`
|
|
409
|
-
* shrinks
|
|
409
|
+
* shrinks, and never larger than `largest`. Idle sessions render nothing.
|
|
410
410
|
*/
|
|
411
|
-
export function chipLine(view, frame, palette, budget) {
|
|
411
|
+
export function chipLine(view, frame, palette, budget, largest = "full") {
|
|
412
412
|
if (!view.active)
|
|
413
413
|
return [];
|
|
414
|
-
const tier = chipTier(view, frame, palette, budget);
|
|
414
|
+
const tier = chipTier(view, frame, palette, budget, largest);
|
|
415
415
|
const groups = chipGroups(view, frame, palette, tier);
|
|
416
416
|
if (tier === "full") {
|
|
417
417
|
const live = liveSegments(view, frame, palette, 24);
|
package/dist/v2/events.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export type EventModelLike = {
|
|
|
36
36
|
};
|
|
37
37
|
export type V2EventOptions = {
|
|
38
38
|
readonly projectRoot: string;
|
|
39
|
-
readonly paths: Pick<RuntimePaths, "autoHandoffFile"
|
|
39
|
+
readonly paths: Pick<RuntimePaths, "autoHandoffFile"> & Partial<Pick<RuntimePaths, "noticesFile">>;
|
|
40
40
|
readonly autoHandoffConfig: AutoHandoffConfig;
|
|
41
41
|
readonly boundedAuthority: BoundedAuthorityOptions;
|
|
42
42
|
readonly getSession: SessionLookup;
|
package/dist/v2/events.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createAutoHandoff } from "../auto-handoff-hooks.js";
|
|
2
2
|
import { LifecycleConflictError, LifecycleSafetyError, createBoundedAuthorityLifecycle, parseFencedProgress } from "../bounded-authority.js";
|
|
3
3
|
import { markLiveness } from "../session-liveness.js";
|
|
4
|
+
import { appendNotice } from "./notices.js";
|
|
4
5
|
function isRecord(value) {
|
|
5
6
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
7
|
}
|
|
@@ -9,10 +10,20 @@ function str(value) {
|
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* The calls the OpenCode 1 automatic handoff makes, answered by OpenCode 2. There is no server toast on
|
|
12
|
-
* OpenCode 2, so
|
|
13
|
+
* OpenCode 2, so `tui.showToast` leaves the notice in the project's notices file, which the TUI plugin
|
|
14
|
+
* reads and shows (notices.ts). Without a notices file the call is accepted and dropped.
|
|
13
15
|
*/
|
|
14
|
-
function handoffClient(session, model, statuses) {
|
|
16
|
+
function handoffClient(session, model, statuses, noticesFile) {
|
|
15
17
|
const client = {
|
|
18
|
+
tui: {
|
|
19
|
+
async showToast(input) {
|
|
20
|
+
const body = input.body;
|
|
21
|
+
if (noticesFile !== undefined && body !== undefined && typeof body.message === "string") {
|
|
22
|
+
appendNotice(noticesFile, { title: body.title ?? "๐ฅ LIT", message: body.message, variant: (body.variant ?? "info"), duration: body.duration ?? 8000 });
|
|
23
|
+
}
|
|
24
|
+
return { data: true };
|
|
25
|
+
}
|
|
26
|
+
},
|
|
16
27
|
session: {
|
|
17
28
|
async promptAsync(input) {
|
|
18
29
|
const text = [input.body.system, ...(input.body.parts ?? []).map((part) => part.text)].filter((part) => typeof part === "string" && part !== "").join("\n\n");
|
|
@@ -50,7 +61,7 @@ export function createV2Events(options) {
|
|
|
50
61
|
const autoHandoff = createAutoHandoff({
|
|
51
62
|
projectRoot: options.projectRoot,
|
|
52
63
|
paths: options.paths,
|
|
53
|
-
client: handoffClient(options.session, options.model, statuses),
|
|
64
|
+
client: handoffClient(options.session, options.model, statuses, options.paths.noticesFile),
|
|
54
65
|
config: options.autoHandoffConfig,
|
|
55
66
|
getSession: options.getSession
|
|
56
67
|
});
|
|
@@ -88,6 +99,11 @@ export function createV2Events(options) {
|
|
|
88
99
|
throw error;
|
|
89
100
|
}
|
|
90
101
|
}
|
|
102
|
+
async function idle(sessionID) {
|
|
103
|
+
statuses.set(sessionID, { type: "idle" });
|
|
104
|
+
await markLiveness(options.projectRoot, sessionID, "idle");
|
|
105
|
+
await toHandoff("session.idle", { sessionID });
|
|
106
|
+
}
|
|
91
107
|
async function handle(event) {
|
|
92
108
|
const data = isRecord(event.data) ? event.data : {};
|
|
93
109
|
const sessionID = str(data.sessionID);
|
|
@@ -135,13 +151,23 @@ export function createV2Events(options) {
|
|
|
135
151
|
});
|
|
136
152
|
return;
|
|
137
153
|
}
|
|
154
|
+
// OpenCode 2.2.5 publishes no `session.status` or `session.idle`. A turn runs from
|
|
155
|
+
// `session.execution.started` to one of three endings, and the handoff reads them as busy and idle.
|
|
156
|
+
case "session.execution.started":
|
|
157
|
+
statuses.set(sessionID, { type: "busy" });
|
|
158
|
+
await toHandoff("session.status", { sessionID, status: { type: "busy" } });
|
|
159
|
+
return;
|
|
160
|
+
case "session.execution.succeeded":
|
|
161
|
+
case "session.execution.failed":
|
|
162
|
+
case "session.execution.interrupted":
|
|
163
|
+
await idle(sessionID);
|
|
164
|
+
return;
|
|
138
165
|
case "session.status":
|
|
139
166
|
statuses.set(sessionID, data.status);
|
|
140
167
|
await toHandoff("session.status", { sessionID, status: data.status });
|
|
141
168
|
return;
|
|
142
169
|
case "session.idle":
|
|
143
|
-
await
|
|
144
|
-
await toHandoff("session.idle", { sessionID });
|
|
170
|
+
await idle(sessionID);
|
|
145
171
|
return;
|
|
146
172
|
case "session.compaction.ended":
|
|
147
173
|
await toHandoff("session.compacted", { sessionID });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type NoticeVariant = "info" | "success" | "warning" | "error";
|
|
2
|
+
export type TuiNotice = {
|
|
3
|
+
readonly title: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly variant: NoticeVariant;
|
|
6
|
+
readonly duration: number;
|
|
7
|
+
};
|
|
8
|
+
/** Leaves one popup for the TUI. Best effort: a place that cannot be written loses the notice, never the caller. */
|
|
9
|
+
export declare function appendNotice(file: string, notice: TuiNotice): void;
|
|
10
|
+
/** Reads the notices that appear after it was made. Each call returns the new ones once, oldest first. */
|
|
11
|
+
export declare function createNoticeTail(file: string): {
|
|
12
|
+
readonly poll: () => readonly TuiNotice[];
|
|
13
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// The way a server-side popup reaches the screen on OpenCode 2. The server plugin's context only subscribes to
|
|
2
|
+
// events, so it cannot raise a toast; the TUI plugin can, but it knows nothing of the server's state machines
|
|
3
|
+
// (the automatic handoff). The server therefore appends each popup it would have shown to a small file under
|
|
4
|
+
// the project's .litopencode, and the TUI reads the lines that appear after it started and toasts them. Both
|
|
5
|
+
// halves ship in this package; neither needs the other to be there. A symbolic link in place of the file is
|
|
6
|
+
// never followed, a torn line waits for its newline, and the file is rewritten to its newest lines past a cap.
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
const variants = new Set(["info", "success", "warning", "error"]);
|
|
10
|
+
const titleCells = 80;
|
|
11
|
+
const messageCells = 400;
|
|
12
|
+
const minDuration = 1000;
|
|
13
|
+
const maxDuration = 15_000;
|
|
14
|
+
const defaultDuration = 8000;
|
|
15
|
+
const capBytes = 64_000;
|
|
16
|
+
const keepBytes = 24_000;
|
|
17
|
+
const readCapBytes = 128_000;
|
|
18
|
+
const seenLimit = 64;
|
|
19
|
+
let counter = 0;
|
|
20
|
+
function clip(value, max) {
|
|
21
|
+
const chars = [...value];
|
|
22
|
+
return chars.length <= max ? value : `${chars.slice(0, max - 1).join("")}โฆ`;
|
|
23
|
+
}
|
|
24
|
+
function clean(value) {
|
|
25
|
+
if (typeof value !== "object" || value === null)
|
|
26
|
+
return undefined;
|
|
27
|
+
const entry = value;
|
|
28
|
+
if (typeof entry.title !== "string" || typeof entry.message !== "string" || entry.message === "")
|
|
29
|
+
return undefined;
|
|
30
|
+
if (typeof entry.variant !== "string" || !variants.has(entry.variant))
|
|
31
|
+
return undefined;
|
|
32
|
+
const duration = typeof entry.duration === "number" && Number.isFinite(entry.duration) ? Math.min(maxDuration, Math.max(minDuration, entry.duration)) : defaultDuration;
|
|
33
|
+
return { title: clip(entry.title, titleCells), message: clip(entry.message, messageCells), variant: entry.variant, duration };
|
|
34
|
+
}
|
|
35
|
+
function plainFile(file) {
|
|
36
|
+
try {
|
|
37
|
+
const stat = fs.lstatSync(file);
|
|
38
|
+
return stat.isFile() ? stat : undefined;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function trimToNewest(file) {
|
|
45
|
+
const text = fs.readFileSync(file, "utf8");
|
|
46
|
+
const lines = text.split("\n").filter((line) => line !== "");
|
|
47
|
+
const kept = [];
|
|
48
|
+
let size = 0;
|
|
49
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
50
|
+
size += Buffer.byteLength(lines[index]) + 1;
|
|
51
|
+
if (size > keepBytes)
|
|
52
|
+
break;
|
|
53
|
+
kept.unshift(lines[index]);
|
|
54
|
+
}
|
|
55
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
56
|
+
fs.writeFileSync(temp, kept.length === 0 ? "" : `${kept.join("\n")}\n`);
|
|
57
|
+
fs.renameSync(temp, file);
|
|
58
|
+
}
|
|
59
|
+
/** Leaves one popup for the TUI. Best effort: a place that cannot be written loses the notice, never the caller. */
|
|
60
|
+
export function appendNotice(file, notice) {
|
|
61
|
+
const cleaned = clean(notice);
|
|
62
|
+
if (cleaned === undefined)
|
|
63
|
+
return;
|
|
64
|
+
try {
|
|
65
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
66
|
+
const existing = (() => {
|
|
67
|
+
try {
|
|
68
|
+
return fs.lstatSync(file);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
if (existing !== undefined && !existing.isFile())
|
|
75
|
+
return;
|
|
76
|
+
counter += 1;
|
|
77
|
+
const stored = { id: `${Date.now().toString(36)}-${process.pid}-${counter}`, at: Date.now(), ...cleaned };
|
|
78
|
+
fs.appendFileSync(file, `${JSON.stringify(stored)}\n`);
|
|
79
|
+
if ((plainFile(file)?.size ?? 0) > capBytes)
|
|
80
|
+
trimToNewest(file);
|
|
81
|
+
}
|
|
82
|
+
catch { // no-excuse-ok: catch -- a popup is best-effort feedback; the handoff itself must never stop for it.
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Reads the notices that appear after it was made. Each call returns the new ones once, oldest first. */
|
|
86
|
+
export function createNoticeTail(file) {
|
|
87
|
+
let offset = plainFile(file)?.size ?? 0;
|
|
88
|
+
const seen = [];
|
|
89
|
+
return {
|
|
90
|
+
poll() {
|
|
91
|
+
const stat = plainFile(file);
|
|
92
|
+
if (stat === undefined)
|
|
93
|
+
return [];
|
|
94
|
+
// A smaller file was rewritten to its newest lines; read it again and let the ids drop what was shown.
|
|
95
|
+
if (stat.size < offset)
|
|
96
|
+
offset = 0;
|
|
97
|
+
if (stat.size === offset)
|
|
98
|
+
return [];
|
|
99
|
+
const length = Math.min(stat.size - offset, readCapBytes);
|
|
100
|
+
const buffer = Buffer.alloc(length);
|
|
101
|
+
let fd;
|
|
102
|
+
try {
|
|
103
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
104
|
+
fs.readSync(fd, buffer, 0, length, offset);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
if (fd !== undefined)
|
|
111
|
+
fs.closeSync(fd);
|
|
112
|
+
}
|
|
113
|
+
const text = buffer.toString("utf8");
|
|
114
|
+
const end = text.lastIndexOf("\n");
|
|
115
|
+
if (end < 0)
|
|
116
|
+
return length === readCapBytes ? (offset += length, []) : [];
|
|
117
|
+
offset += Buffer.byteLength(text.slice(0, end + 1));
|
|
118
|
+
const out = [];
|
|
119
|
+
for (const line of text.slice(0, end).split("\n")) {
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = JSON.parse(line);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const id = typeof parsed?.id === "string" ? parsed.id : undefined;
|
|
128
|
+
if (id !== undefined && seen.includes(id))
|
|
129
|
+
continue;
|
|
130
|
+
const entry = clean(parsed);
|
|
131
|
+
if (entry === undefined)
|
|
132
|
+
continue;
|
|
133
|
+
if (id !== undefined) {
|
|
134
|
+
seen.push(id);
|
|
135
|
+
if (seen.length > seenLimit)
|
|
136
|
+
seen.shift();
|
|
137
|
+
}
|
|
138
|
+
out.push(entry);
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MissionView } from "../tui/mission-model.ts";
|
|
2
|
+
import type { MotionFrame } from "../tui/mission-motion.ts";
|
|
3
|
+
import { type Line, type MissionPalette } from "../tui/mission-text.ts";
|
|
4
|
+
/** The session sidebar, which a wide terminal is taken to have open (the setting is not readable by a plugin). */
|
|
5
|
+
export declare const sidebarCells = 42;
|
|
6
|
+
export declare function footerBudget(input: {
|
|
7
|
+
readonly columns: number;
|
|
8
|
+
readonly sidebar: boolean;
|
|
9
|
+
readonly busy: boolean;
|
|
10
|
+
}): number;
|
|
11
|
+
export type FooterInput = {
|
|
12
|
+
/** The discipline the latest lit prompt started, as the server wrote it into the message. */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly view: MissionView | undefined;
|
|
15
|
+
readonly frame: MotionFrame;
|
|
16
|
+
readonly palette: MissionPalette;
|
|
17
|
+
readonly budget: number;
|
|
18
|
+
};
|
|
19
|
+
/** The footer line for `budget` cells, as segments; empty when not even the flame fits. */
|
|
20
|
+
export declare function footerStatus(input: FooterInput): Line;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// What OpenCode 2's prompt footer row holds besides our line, and how our line steps down to the room left.
|
|
2
|
+
// The row is the host's spinner and `esc interrupt` hint on the left and its token, goal and `ctrl+p commands`
|
|
3
|
+
// figures on the right; our line is squeezed between them, and the host clips it mid-word when it is too long.
|
|
4
|
+
// So the line is chosen by width: the ignition mark with the whole stage chip, the chip at a smaller size,
|
|
5
|
+
// the chip as `๐ฅ 1/3`, the bare flame, then nothing. The numbers were measured on opencode2 2.2.5.
|
|
6
|
+
import { activationDiscipline } from "../activation-probe.js";
|
|
7
|
+
import { cellWidth, chipLine, lineWidth } from "../tui/mission-text.js";
|
|
8
|
+
/** The session sidebar, which a wide terminal is taken to have open (the setting is not readable by a plugin). */
|
|
9
|
+
export const sidebarCells = 42;
|
|
10
|
+
// Right of our line: with a turn running `โ 1 shell ยท โ goal ctrl+p commands`, idle `โ goal paused ยท 42.3K (8%) ยท $0.07 ctrl+p commands`.
|
|
11
|
+
const hostRightBusy = 38;
|
|
12
|
+
const hostRightIdle = 54;
|
|
13
|
+
// Left of it, the least the host keeps: the spinner and a shortened `esc โฆrrupt` while running, a shortened path when idle.
|
|
14
|
+
const hostLeftBusy = 24;
|
|
15
|
+
const hostLeftIdle = 6;
|
|
16
|
+
const gap = 2;
|
|
17
|
+
export function footerBudget(input) {
|
|
18
|
+
const main = input.columns - (input.sidebar ? sidebarCells : 0);
|
|
19
|
+
const host = input.busy ? hostRightBusy + hostLeftBusy : hostRightIdle + hostLeftIdle;
|
|
20
|
+
return Math.max(0, main - host);
|
|
21
|
+
}
|
|
22
|
+
const flameWidth = 2;
|
|
23
|
+
/** The footer line for `budget` cells, as segments; empty when not even the flame fits. */
|
|
24
|
+
export function footerStatus(input) {
|
|
25
|
+
const { palette, budget } = input;
|
|
26
|
+
const discipline = activationDiscipline(input.name);
|
|
27
|
+
const mark = (text) => [{ text, fg: palette.primary, bold: true }];
|
|
28
|
+
const full = `๐ฅ LIT IGNITED ยท ${discipline}`;
|
|
29
|
+
const active = input.view !== undefined && input.view.active;
|
|
30
|
+
const chipFor = (room, largest) => {
|
|
31
|
+
if (!active || input.view === undefined || room < flameWidth)
|
|
32
|
+
return [];
|
|
33
|
+
const chip = chipLine(input.view, input.frame, palette, room, largest);
|
|
34
|
+
return lineWidth(chip) <= room ? chip : [];
|
|
35
|
+
};
|
|
36
|
+
if (!active) {
|
|
37
|
+
if (cellWidth(full) <= budget)
|
|
38
|
+
return mark(full);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const beside = chipFor(budget - cellWidth(full) - gap, "full");
|
|
42
|
+
if (beside.length > 0)
|
|
43
|
+
return [...mark(full), { text: " ".repeat(gap), fg: palette.muted }, ...beside];
|
|
44
|
+
// The chip is worth more than the mark's words, so it stays alone when both do not fit. Alone it never
|
|
45
|
+
// grows past the middle size, so the line only gets smaller as the room does.
|
|
46
|
+
const alone = chipFor(budget, "medium");
|
|
47
|
+
if (alone.length > 0)
|
|
48
|
+
return alone;
|
|
49
|
+
}
|
|
50
|
+
const short = `๐ฅ ${discipline}`;
|
|
51
|
+
if (cellWidth(short) <= budget)
|
|
52
|
+
return mark(short);
|
|
53
|
+
if (budget >= flameWidth)
|
|
54
|
+
return mark("๐ฅ");
|
|
55
|
+
return [];
|
|
56
|
+
}
|
package/dist/v2/tui.d.ts
CHANGED
|
@@ -38,6 +38,12 @@ export type TuiContextLike = {
|
|
|
38
38
|
} | undefined;
|
|
39
39
|
readonly theme: unknown;
|
|
40
40
|
readonly themeMode: "dark" | "light";
|
|
41
|
+
/** The host's API client; only `usage.limits` is read, and a host without it simply shows no plan figures. */
|
|
42
|
+
readonly client?: {
|
|
43
|
+
readonly usage?: {
|
|
44
|
+
readonly limits?: () => Promise<unknown>;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
41
47
|
readonly attention?: {
|
|
42
48
|
readonly notify: (options: {
|
|
43
49
|
readonly title?: string;
|
package/dist/v2/tui.js
CHANGED
|
@@ -13,15 +13,19 @@ import { micro, supportsMarkGlyphs } from "../lit-mark.js";
|
|
|
13
13
|
import { readInstalledVersions, restartVersion } from "../restart-notice.js";
|
|
14
14
|
import { buildMissionView, lastMission, leadingNotice, missionNotices } from "../tui/mission-model.js";
|
|
15
15
|
import { idleMotion, motionTiming, planMotion, sampleMotion } from "../tui/mission-motion.js";
|
|
16
|
-
import { latestLitMarkV2, missionStateFromV2 } from "../tui/mission-source-v2.js";
|
|
16
|
+
import { latestLitMarkV2, missionStateFromV2, sessionMessagesV2 } from "../tui/mission-source-v2.js";
|
|
17
17
|
import { adhdSuggestDue, createLedgerReader, createWorkReader, earlierWorkWindowMs, hasEarlierWork, messageTimes, missionProjectRoot, readCardInput, readLatestResearchRun, readMissionInput, readThreadSources, wayBackDue } from "../tui/mission-source.js";
|
|
18
18
|
import { chipShimmer, idleChipMotion, planChipMotion } from "../tui/mission-skills.js";
|
|
19
19
|
import { bandCard, readBandOptions, readNotifyOptions, statusCard } from "../tui/mission-band.js";
|
|
20
20
|
import { activeSkillLook, turnGlow, waitingOnYou } from "../tui/mission-looks.js";
|
|
21
21
|
import { rainbowActive, rainbowAllowed, rainbowAt, rainbowShows, rainbowTickMs } from "../tui/mission-rainbow.js";
|
|
22
22
|
import { threadLineCount, threadsOf } from "../tui/mission-threads.js";
|
|
23
|
-
import {
|
|
23
|
+
import { homeLine, missionPanel, paletteFromTheme, panelWidth } from "../tui/mission-text.js";
|
|
24
|
+
import { createRuntimePaths } from "../state.js";
|
|
25
|
+
import { footerBudget, footerStatus, sidebarCells } from "./tui-footer.js";
|
|
26
|
+
import { createNoticeTail } from "./notices.js";
|
|
24
27
|
import { v2StageNotices } from "./tui-notices.js";
|
|
28
|
+
import { createUsageLimits } from "./usage-limits.js";
|
|
25
29
|
const refreshDelayMs = 80;
|
|
26
30
|
const ledgerPollMs = 2000;
|
|
27
31
|
const toastMs = 4000;
|
|
@@ -33,11 +37,12 @@ const homeLineWidth = 69;
|
|
|
33
37
|
// A lit mark older than the moment a session is first drawn is history being loaded, not a new turn.
|
|
34
38
|
const historySlackMs = 2000;
|
|
35
39
|
const defaultWidth = 120;
|
|
36
|
-
// What the footer row holds besides our line: the host's spinner and hint on the left, its token, cost,
|
|
37
|
-
// goal and `ctrl+p commands` figures on the right (measured with a turn running, when they are widest).
|
|
38
|
-
const hostFooterCells = 68;
|
|
39
|
-
const sidebarCells = 42;
|
|
40
40
|
const sidebarMinWidth = 120;
|
|
41
|
+
// The plan windows change slowly; asking the host more than once a minute would only add requests.
|
|
42
|
+
const limitsPollMs = 60_000;
|
|
43
|
+
// Only the OpenCode Go allowance has a source on OpenCode 2, so the windows belong to a Go session alone.
|
|
44
|
+
const goProviderID = "opencode-go";
|
|
45
|
+
const noticeBurst = 3;
|
|
41
46
|
async function hostRuntime() {
|
|
42
47
|
const [jsxRuntime, solid, opentui] = await Promise.all([import("@opentui/solid/jsx-runtime"), import("solid-js"), import("@opentui/solid")]);
|
|
43
48
|
return {
|
|
@@ -95,6 +100,27 @@ export async function setupLitOpenCodeTui(context, runtime) {
|
|
|
95
100
|
let liveTimer;
|
|
96
101
|
let bandTimer;
|
|
97
102
|
let restartTimer;
|
|
103
|
+
const readUsage = context.client?.usage?.limits;
|
|
104
|
+
const limits = createUsageLimits({
|
|
105
|
+
read: readUsage === undefined ? undefined : () => readUsage.call(context.client?.usage),
|
|
106
|
+
now: Date.now,
|
|
107
|
+
intervalMs: limitsPollMs,
|
|
108
|
+
onChange: () => setRevision(revision() + 1)
|
|
109
|
+
});
|
|
110
|
+
const noticeTail = createNoticeTail(root === undefined ? "" : createRuntimePaths(root).noticesFile);
|
|
111
|
+
// The provider of the session's latest assistant message decides whether the Go allowance applies to it.
|
|
112
|
+
function goSession(sessionID) {
|
|
113
|
+
const latest = sessionMessagesV2(context.data, sessionID).filter((message) => message.type === "assistant").at(-1);
|
|
114
|
+
return latest?.model?.providerID === goProviderID;
|
|
115
|
+
}
|
|
116
|
+
// Popups the server could not raise (the automatic handoff) arrive as lines in the notices file.
|
|
117
|
+
function showServerNotices() {
|
|
118
|
+
if (disposed)
|
|
119
|
+
return;
|
|
120
|
+
for (const notice of noticeTail.poll().slice(-noticeBurst)) {
|
|
121
|
+
context.ui.toast.show({ title: notice.title, message: notice.message, variant: notice.variant, duration: notice.duration });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
98
124
|
const palette = () => paletteFromV2Theme(context.theme, context.themeMode);
|
|
99
125
|
function span(segment) {
|
|
100
126
|
return ui.jsx("span", {
|
|
@@ -264,6 +290,8 @@ export async function setupLitOpenCodeTui(context, runtime) {
|
|
|
264
290
|
if (mark !== undefined && mark.at >= (trackedAt.get(sessionID) ?? 0) - historySlackMs)
|
|
265
291
|
ignitionToast(sessionID, mark.name);
|
|
266
292
|
}
|
|
293
|
+
if (goSession(sessionID))
|
|
294
|
+
void limits.poll();
|
|
267
295
|
const entry = sessions.get(sessionID);
|
|
268
296
|
const { next, changed, notices } = build(sessionID, entry, now);
|
|
269
297
|
if (!changed)
|
|
@@ -299,6 +327,7 @@ export async function setupLitOpenCodeTui(context, runtime) {
|
|
|
299
327
|
return;
|
|
300
328
|
for (const sessionID of tracked)
|
|
301
329
|
refresh(sessionID);
|
|
330
|
+
showServerNotices();
|
|
302
331
|
syncPoll();
|
|
303
332
|
}
|
|
304
333
|
function scheduleRefresh() {
|
|
@@ -379,6 +408,7 @@ export async function setupLitOpenCodeTui(context, runtime) {
|
|
|
379
408
|
contextPercent: current.card.contextPercent,
|
|
380
409
|
...(current.card.branch === undefined ? {} : { branch: current.card.branch }),
|
|
381
410
|
inLane,
|
|
411
|
+
...(goSession(sessionID) && limits.windows().length > 0 ? { limits: limits.windows() } : {}),
|
|
382
412
|
...(restartTo() === undefined ? {} : { restartTo: restartTo() })
|
|
383
413
|
},
|
|
384
414
|
...(last === undefined ? {} : { last })
|
|
@@ -415,19 +445,24 @@ export async function setupLitOpenCodeTui(context, runtime) {
|
|
|
415
445
|
const mark = latestLitMarkV2(context.data, sessionID);
|
|
416
446
|
if (mark === undefined)
|
|
417
447
|
return null;
|
|
418
|
-
const ignition = ignitionLine(mark.name);
|
|
419
448
|
const current = snapshot(sessionID);
|
|
420
449
|
// The sidebar setting is not readable here, so a wide terminal is taken to have it open.
|
|
421
|
-
const sidebar = parentOf(sessionID) === undefined && columns() > sidebarMinWidth
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
450
|
+
const sidebar = parentOf(sessionID) === undefined && columns() > sidebarMinWidth;
|
|
451
|
+
const footer = footerStatus({
|
|
452
|
+
name: mark.name,
|
|
453
|
+
view: current?.view,
|
|
454
|
+
frame: current?.frame ?? sampleMotion(idleMotion, clock()),
|
|
455
|
+
palette: palette(),
|
|
456
|
+
budget: footerBudget({ columns: columns(), sidebar, busy: current?.card.busy === true })
|
|
457
|
+
});
|
|
458
|
+
if (footer.length === 0)
|
|
459
|
+
return null;
|
|
425
460
|
return ui.jsx("box", {
|
|
426
461
|
flexShrink: 1,
|
|
427
462
|
minWidth: 0,
|
|
428
463
|
overflow: "hidden",
|
|
429
464
|
flexDirection: "row",
|
|
430
|
-
children:
|
|
465
|
+
children: line(footer)
|
|
431
466
|
});
|
|
432
467
|
};
|
|
433
468
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type LimitWindow = {
|
|
2
|
+
readonly label: "5h" | "1w" | "1mo";
|
|
3
|
+
readonly percent: number;
|
|
4
|
+
readonly limited: boolean;
|
|
5
|
+
};
|
|
6
|
+
/** Reads the usage-limits reply (`{ data: Info | null }` or the bare Info) into the windows worth drawing. */
|
|
7
|
+
export declare function limitWindows(reply: unknown): readonly LimitWindow[];
|
|
8
|
+
export type UsageLimitsOptions = {
|
|
9
|
+
/** Asks the host; absent when the host has no usage source. */
|
|
10
|
+
readonly read: (() => Promise<unknown>) | undefined;
|
|
11
|
+
readonly now: () => number;
|
|
12
|
+
readonly intervalMs: number;
|
|
13
|
+
readonly onChange: () => void;
|
|
14
|
+
};
|
|
15
|
+
/** A throttled reader: it asks at most once per interval, and a failed or empty answer clears the figures. */
|
|
16
|
+
export declare function createUsageLimits(options: UsageLimitsOptions): {
|
|
17
|
+
readonly windows: () => readonly LimitWindow[];
|
|
18
|
+
readonly poll: () => Promise<void>;
|
|
19
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// The plan windows OpenCode 2 can tell a plugin about. Its one usage source is client.usage.limits(): the
|
|
2
|
+
// OpenCode Go allowance of the Console workspace the location is signed in to, as a rolling window (five
|
|
3
|
+
// hours), a weekly one and a monthly one, each with a percent used; the reply is null when nobody is signed
|
|
4
|
+
// in. There are no provider response headers or rate-limit events for a plugin to read, so for any other
|
|
5
|
+
// provider this module has nothing to show, and the card draws no figure rather than a made-up one.
|
|
6
|
+
/** The monthly window is shown only when it is close to the cap, so the card keeps LitClaude's 5h and 1w pair. */
|
|
7
|
+
const monthlyShowPercent = 90;
|
|
8
|
+
function record(value) {
|
|
9
|
+
return typeof value === "object" && value !== null ? value : undefined;
|
|
10
|
+
}
|
|
11
|
+
function window(label, raw) {
|
|
12
|
+
const meter = record(raw);
|
|
13
|
+
const percent = meter?.percent;
|
|
14
|
+
if (typeof percent !== "number" || !Number.isFinite(percent))
|
|
15
|
+
return undefined;
|
|
16
|
+
return { label, percent: Math.round(Math.min(100, Math.max(0, percent))), limited: meter?.limited === true };
|
|
17
|
+
}
|
|
18
|
+
/** Reads the usage-limits reply (`{ data: Info | null }` or the bare Info) into the windows worth drawing. */
|
|
19
|
+
export function limitWindows(reply) {
|
|
20
|
+
const outer = record(reply);
|
|
21
|
+
const info = outer === undefined ? undefined : "data" in outer ? record(outer.data) : outer;
|
|
22
|
+
const allowance = record(info?.go);
|
|
23
|
+
if (allowance === undefined)
|
|
24
|
+
return [];
|
|
25
|
+
const rolling = window("5h", allowance.rolling);
|
|
26
|
+
const weekly = window("1w", allowance.weekly);
|
|
27
|
+
const monthly = window("1mo", allowance.monthly);
|
|
28
|
+
return [
|
|
29
|
+
...(rolling === undefined ? [] : [rolling]),
|
|
30
|
+
...(weekly === undefined ? [] : [weekly]),
|
|
31
|
+
...(monthly !== undefined && (monthly.limited || monthly.percent >= monthlyShowPercent) ? [monthly] : [])
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
/** A throttled reader: it asks at most once per interval, and a failed or empty answer clears the figures. */
|
|
35
|
+
export function createUsageLimits(options) {
|
|
36
|
+
let current = [];
|
|
37
|
+
let askedAt;
|
|
38
|
+
let pending;
|
|
39
|
+
async function ask(read) {
|
|
40
|
+
let next;
|
|
41
|
+
try {
|
|
42
|
+
next = limitWindows(await read());
|
|
43
|
+
}
|
|
44
|
+
catch { // no-excuse-ok: catch -- an unreachable or signed-out host means there is nothing to show, never an error on screen.
|
|
45
|
+
next = [];
|
|
46
|
+
}
|
|
47
|
+
if (JSON.stringify(next) === JSON.stringify(current))
|
|
48
|
+
return;
|
|
49
|
+
current = next;
|
|
50
|
+
options.onChange();
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
windows: () => current,
|
|
54
|
+
poll() {
|
|
55
|
+
const read = options.read;
|
|
56
|
+
if (read === undefined)
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
if (pending !== undefined)
|
|
59
|
+
return pending;
|
|
60
|
+
const now = options.now();
|
|
61
|
+
if (askedAt !== undefined && now - askedAt < options.intervalMs)
|
|
62
|
+
return Promise.resolve();
|
|
63
|
+
askedAt = now;
|
|
64
|
+
pending = ask(read).finally(() => {
|
|
65
|
+
pending = undefined;
|
|
66
|
+
});
|
|
67
|
+
return pending;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.38"><title>release: 1.0.38</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.38</text></g></svg>
|
package/docs/reference-Ko-KR.md
CHANGED
|
@@ -384,14 +384,25 @@ Code Mode ๋ฐ, ๋ชจ๋ธ์ ๋๊ตฌ ๋ชฉ๋ก์ ๋ฐ๋ก ์์ผ๋ฏ๋ก `lit-plan`๋ ๊ณ
|
|
|
384
384
|
๋ํด OpenCode 2์๋ง ์๋ ์ธ ๊ฐ์ง๋ฅผ ๋์๋๋ค. ์์
๋จ๊ณ๊ฐ ๋์ด๊ฐ ๋(`๐ฅ Stage`, `work 3/7`), ๊ท์จ์ด ๋ฐ๋ ๋
|
|
385
385
|
(`๐ฅ lit-loop โ start-work`), ๋ ์ธ์ด ์์๋๊ฑฐ๋ ๋๋ ๋์
๋๋ค. ํ๊บผ๋ฒ์ ์ผ์ด๋ ์ผ์ `+N more`๊ฐ ๋ถ์ ํ ์คํธ
|
|
386
386
|
ํ๋๋ก ํฉ์นฉ๋๋ค. ์ฌ์์ ์๋ด, `lit adhd` ์ ์, ์ผ ๋๋ฉด ๋์ค๋ ์๋ฃ ์๋ฆผ์ OpenCode 1๊ณผ ๊ฐ์ต๋๋ค. ์๋ฒ๋ง ์๋
|
|
387
|
-
์๋ ์ธ์์ธ๊ณ
|
|
387
|
+
์๋ ์ธ์์ธ๊ณ ์๋ฆผ์ `.litopencode/notices.jsonl`์ ๊ฑฐ์ณ ํ๋ฉด์ ๋์ต๋๋ค. OpenCode 2 ์๋ฒ ํ๋ฌ๊ทธ์ธ์ ์ด๋ฒคํธ๋ฅผ
|
|
388
|
+
๊ตฌ๋
ํ ์๋ง ์๊ณ ํ ์คํธ๋ฅผ ๋์ธ ์ ์์ผ๋ฏ๋ก, ์๋ฒ๊ฐ ์๋ฆผ์ ๊ทธ ํ์ผ์ ํ ์ค์ฉ ๋ง๋ถ์ด๋ฉด TUI ํ๋ฌ๊ทธ์ธ์ด ์์ํ ๋ค์
|
|
389
|
+
์๊ธด ์ค์ ํ ์คํธ๋ก ๋ณด์ฌ ์ค๋๋ค(ํ์ผ ํฌ๊ธฐ์ ์ํ์ด ์๊ณ ์ฌ๋ณผ๋ฆญ ๋งํฌ๋ ๋ฐ๋ผ๊ฐ์ง ์์ต๋๋ค). ์๋ฒ๊ฐ ๋์ฐ๋ ๋๋จธ์ง
|
|
390
|
+
ํ๋์ธ ์ ํ ํ ์คํธ๋ ์ด๋ฏธ TUI๊ฐ ์ง์ ๋์ฐ๋ฏ๋ก, ๋ฐ๋ก ์ฎ๊ธธ ์คํฌ ํํธ๋ ๋จ์ ์์ง ์์ต๋๋ค.
|
|
388
391
|
- *TUI.* HUD๋ OpenCode 2๊ฐ ๊ณต๊ฐํ ์ฌ๋กฏ์ ๋์
๋๋ค. ์ํ ์นด๋๋ `app` ์ฌ๋กฏ์์ ์ธ์
๊ณผ ์ฌ์ด๋๋ฐ ์๋์ ๊ทธ๋ ค์ง๊ณ ,
|
|
389
392
|
์ฌ๋ ์ผํ๋ ๋ค ์ค์ ์ ์งํฉ๋๋ค. ๋จ๊ณ ์นฉ์ ์์ด์ ํธยท๋ชจ๋ธ ์ค ์ค๋ฅธ์ชฝ์ ์ฌ๋กฏ์ด ์์ด์ ์ ํ ์ค๊ณผ ํจ๊ป
|
|
390
393
|
`prompt.footer.status`์ ์๊ณ , ํ ์ค์ `home.footer`, ๋ฏธ์
ํจ๋์ `sidebar.content`์ ์์ผ๋ฏ๋ก ์ฌ์ด๋๋ฐ๋ฅผ ์ด์ด
|
|
391
394
|
๋๋ฉด ๋ณด์
๋๋ค. ์นด๋, ์นฉ, ํจ๋, ํ ์ค์ OpenCode 1๊ณผ ๊ฐ์ ๊ธ์ ๋น๋๋ฅผ ์๋๋ค. ํ๋ฌ๊ทธ์ธ ํญ๋ชฉ์ `band`, `notify`
|
|
392
|
-
์ต์
์ OpenCode 2๊ฐ ๋๊ฒจ ์ฃผ๋ ์ต์
์์ ์ฝ์ต๋๋ค.
|
|
395
|
+
์ต์
์ OpenCode 2๊ฐ ๋๊ฒจ ์ฃผ๋ ์ต์
์์ ์ฝ์ต๋๋ค. ํธํฐ ์ค์ ํธ์คํธ๊ฐ ์๋ผ ๋ฒ๋ฆฌ๊ฒ ๋์ง ์๊ณ ํฐ๋ฏธ๋ ๋๋น์ ๋ง์ถฐ
|
|
396
|
+
๋จ๊ณ์ ์ผ๋ก ์ค์ด๋ญ๋๋ค. ํ์์ ์ ์ฒด ์นฉ, ํ์์ ์์ ์นฉ, ์นฉ๋ง, `๐ฅ 1/3`, ๋ถ๊ฝ๋ง, ์๋ฌด๊ฒ๋ ์์ ์์์
๋๋ค. ์ธ ์ ์๋
|
|
397
|
+
๋๋น๋ ์ ์ฒด ๋๋น์์ ์ฌ์ด๋๋ฐ(120์นธ์ ๋์ผ๋ฉด ์ด๋ ค ์๋ค๊ณ ๊ฐ์ )์ ํธ์คํธ ์์ ์ ์คํผ๋ยท์์น๋ฅผ ๋บ ๊ฐ์
๋๋ค.
|
|
398
|
+
OpenCode 2๋ ํ ์ผ ๋ชฉ๋ก์ ๋์ง ์์ผ๋ฏ๋ก ๋ฏธ์
์งํ์ ledger์
|
|
393
399
|
์์
๊ธฐ๋ก๊ณผ ๊ณํ์์ ์ฝ์ต๋๋ค. ๋๊ตฌ ์ด๋ฆ๋ ๋ค๋ฆ
๋๋ค(`shell`, `subagent`, `id`๋ฅผ ๋ฐ๋ `skill` ๋๊ตฌ). ์นด๋๋ ๊ทธ
|
|
394
|
-
์ด๋ฆ์ ์ฝ์ต๋๋ค. OpenCode 2
|
|
400
|
+
์ด๋ฆ์ ์ฝ์ต๋๋ค. OpenCode 2๊ฐ ํ๋ฌ๊ทธ์ธ์ ์ฃผ๋ ์ฌ์ฉ๋ ์ถ์ฒ๋ `client.usage.limits()` ํ๋์
๋๋ค. ์์น๊ฐ ๋ก๊ทธ์ธํ
|
|
401
|
+
OpenCode Console ์์
๊ณต๊ฐ์ OpenCode Go ํ์ฉ๋(5์๊ฐ ๋กค๋ง, ์ฃผ๊ฐ, ์๊ฐ ์ฐฝ)์
๋๋ค. ๋ง์ง๋ง ๋ต๋ณ์ด `opencode-go`
|
|
402
|
+
์ ๊ณต์์์ ์จ ์ธ์
์ด๋ฉด ์นด๋ ์๋ ํ
๋๋ฆฌ์ ์ปจํ
์คํธ ์์น ๋ค๋ก `5h 42% ยท 1w 18%`๊ฐ ๋์ต๋๋ค(์๊ฐ ์ฐฝ์ 90% ์ด์์ผ
|
|
403
|
+
๋๋ง, ํธ์คํธ๊ฐ ์์ฒญ์ ๊ฑฐ๋ถ ์ค์ด๋ผ๊ณ ์๋ฆฌ๋ ์ฐฝ์ `limited`๋ก ํ์). ํธ์ถ์ 1๋ถ์ ํ ๋ฒ ์ดํ์
๋๋ค. ๋ค๋ฅธ ์ ๊ณต์,
|
|
404
|
+
๋ก๊ทธ์์ ์ํ(์๋ต์ด `null`), ์ค๋ฅ์ผ ๋๋ ์๋ฌด๊ฒ๋ ๊ทธ๋ฆฌ์ง ์์ต๋๋ค. OpenCode 2๋ ์ ๊ณต์ ์๋ ์ ํ ํค๋๋ ์ด๋ฒคํธ๋ฅผ
|
|
405
|
+
ํ๋ฌ๊ทธ์ธ์ ๋
ธ์ถํ์ง ์์ผ๋ฏ๋ก AnthropicยทOpenAI ์๊ธ์ ์ 5์๊ฐยท์ฃผ๊ฐ ์์น๋ ์์ต๋๋ค.
|
|
395
406
|
- *์
๋ฐ์ดํธ.* OpenCode 2์์๋ ํ๋ฌ๊ทธ์ธ ์์ ์๋ ์
๋ฐ์ดํธ๊ฐ ์์ต๋๋ค. `opencode plugin update`๋ฅผ ์คํํ๊ฑฐ๋
|
|
396
407
|
OpenCode 2์ ์
๋ฐ์ดํธ ๋ฐฉ์์ ๋งก๊ธฐ๋ฉด ๋ฉ๋๋ค.
|
|
397
408
|
|
package/docs/reference.md
CHANGED
|
@@ -503,17 +503,29 @@ settings but does not send them yet.
|
|
|
503
503
|
only OpenCode 2 has: the workflow stage moving (`๐ฅ Stage`, `work 3/7`), a switch of discipline
|
|
504
504
|
(`๐ฅ lit-loop โ start-work`) and a lane starting or finishing. A burst folds into one toast with a
|
|
505
505
|
`+N more` tail. The restart notice, the `lit adhd` suggestion and the opt-in finish alert work as on
|
|
506
|
-
OpenCode 1. The automatic-handoff notices
|
|
507
|
-
|
|
506
|
+
OpenCode 1. The automatic-handoff notices, which only the server knew about, reach the screen through
|
|
507
|
+
`.litopencode/notices.jsonl`: the OpenCode 2 server plugin can only subscribe to events and has no way to
|
|
508
|
+
raise a toast, so it appends each notice there and the TUI plugin shows the lines that appear after it
|
|
509
|
+
started (the file is capped and never follows a symbolic link). The one popup the server raised besides
|
|
510
|
+
those, the ignition toast, is already the TUI's own, so there is no separate skill hint left to port.
|
|
508
511
|
- *TUI.* The HUD is placed in the slots OpenCode 2 publishes: the status card is claimed in `app`, under
|
|
509
512
|
the session and the sidebar, and keeps its four rows idle or working; the stage chip shares
|
|
510
513
|
`prompt.footer.status` with the ignition line, because there is no slot right of the agent and model
|
|
511
514
|
row; the home line is in `home.footer`; the mission panel sits in `sidebar.content`, so it shows when
|
|
512
515
|
the sidebar is open. The card, chip, panel and home line use the same text builders as OpenCode 1. The
|
|
513
|
-
`band` and `notify` options of the plugin entry are read from the options OpenCode 2 hands over.
|
|
516
|
+
`band` and `notify` options of the plugin entry are read from the options OpenCode 2 hands over. The footer
|
|
517
|
+
line steps down with the terminal width instead of being clipped by the host: mark plus the whole chip,
|
|
518
|
+
mark plus a smaller chip, the chip alone, the chip as `๐ฅ 1/3`, the bare flame, then nothing; the room is the
|
|
519
|
+
width less the sidebar (assumed open above 120 columns) and the host's own spinner and figures. OpenCode 2
|
|
514
520
|
keeps no todo list, so mission progress comes from the ledger, the work record and the plan. It names
|
|
515
521
|
its tools differently (`shell`, `subagent`, a `skill` tool that takes `id`), and the card reads those
|
|
516
|
-
names. OpenCode 2 gives plugins
|
|
522
|
+
names. OpenCode 2 gives plugins one usage source, `client.usage.limits()`: the OpenCode Go allowance of the
|
|
523
|
+
Console workspace the location is signed in to (a rolling five-hour window, a weekly one and a monthly
|
|
524
|
+
one). For a session whose latest answer came from the `opencode-go` provider the card's bottom border shows
|
|
525
|
+
`5h 42% ยท 1w 18%` after the context figure (the monthly window only from 90%, and a window the host reports
|
|
526
|
+
as refusing requests reads `limited`). It asks at most once a minute. For any other provider, a signed-out
|
|
527
|
+
host (the reply is `null`) or an error, nothing is drawn; OpenCode 2 exposes no provider rate-limit headers or
|
|
528
|
+
events to a plugin, so there is no 5-hour or weekly figure for the Anthropic or OpenAI plans.
|
|
517
529
|
- *Updates.* There is no automatic self-update inside the plugin on OpenCode 2. Run
|
|
518
530
|
`opencode plugin update`, or let OpenCode 2's own update policy handle it.
|
|
519
531
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litfamily/litopencode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.38",
|
|
4
4
|
"description": "Workflow plugin for OpenCode: add lit to a prompt and the work is planned, built, checked and written down so the next session can pick it up.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"yaml": "^2.9.0",
|
|
73
|
-
"zod": "4.
|
|
73
|
+
"zod": "4.6.6"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@opencode-ai/plugin": "^1.17.6",
|
|
@@ -79,6 +79,10 @@
|
|
|
79
79
|
"picomatch": "^4.0.5",
|
|
80
80
|
"typescript": "^5.9.3"
|
|
81
81
|
},
|
|
82
|
+
"overrides": {
|
|
83
|
+
"zod": "$zod",
|
|
84
|
+
"toml": "5.0.1"
|
|
85
|
+
},
|
|
82
86
|
"publishConfig": {
|
|
83
87
|
"access": "public"
|
|
84
88
|
}
|
|
@@ -391,95 +391,98 @@ llvmlite==0.49.0 \
|
|
|
391
391
|
--hash=sha256:ee81e96c15a6f870918f1eb60c913551c16aa23defb4f5f1acfa660d6a0aaac2 \
|
|
392
392
|
--hash=sha256:f3f2ff0aeb17d34fcce9f79b99baac441cfd3efa41b83e233ca4530a72381f72
|
|
393
393
|
# via numba
|
|
394
|
-
msgpack==1.2.
|
|
395
|
-
--hash=sha256:
|
|
396
|
-
--hash=sha256:
|
|
397
|
-
--hash=sha256:
|
|
398
|
-
--hash=sha256:
|
|
399
|
-
--hash=sha256:
|
|
400
|
-
--hash=sha256:
|
|
401
|
-
--hash=sha256:
|
|
402
|
-
--hash=sha256:
|
|
403
|
-
--hash=sha256:
|
|
404
|
-
--hash=sha256:
|
|
405
|
-
--hash=sha256:
|
|
406
|
-
--hash=sha256:
|
|
407
|
-
--hash=sha256:
|
|
408
|
-
--hash=sha256:
|
|
409
|
-
--hash=sha256:
|
|
410
|
-
--hash=sha256:
|
|
411
|
-
--hash=sha256:
|
|
412
|
-
--hash=sha256:
|
|
413
|
-
--hash=sha256:
|
|
414
|
-
--hash=sha256:
|
|
415
|
-
--hash=sha256:
|
|
416
|
-
--hash=sha256:
|
|
417
|
-
--hash=sha256:
|
|
418
|
-
--hash=sha256:
|
|
419
|
-
--hash=sha256:
|
|
420
|
-
--hash=sha256:
|
|
421
|
-
--hash=sha256:
|
|
422
|
-
--hash=sha256:
|
|
423
|
-
--hash=sha256:
|
|
424
|
-
--hash=sha256:
|
|
425
|
-
--hash=sha256:
|
|
426
|
-
--hash=sha256:
|
|
427
|
-
--hash=sha256:
|
|
428
|
-
--hash=sha256:
|
|
429
|
-
--hash=sha256:
|
|
430
|
-
--hash=sha256:
|
|
431
|
-
--hash=sha256:
|
|
432
|
-
--hash=sha256:
|
|
433
|
-
--hash=sha256:
|
|
434
|
-
--hash=sha256:
|
|
435
|
-
--hash=sha256:
|
|
436
|
-
--hash=sha256:
|
|
437
|
-
--hash=sha256:
|
|
438
|
-
--hash=sha256:
|
|
439
|
-
--hash=sha256:
|
|
440
|
-
--hash=sha256:
|
|
441
|
-
--hash=sha256:
|
|
442
|
-
--hash=sha256:
|
|
443
|
-
--hash=sha256:
|
|
444
|
-
--hash=sha256:
|
|
445
|
-
--hash=sha256:
|
|
446
|
-
--hash=sha256:
|
|
447
|
-
--hash=sha256:
|
|
448
|
-
--hash=sha256:
|
|
449
|
-
--hash=sha256:
|
|
450
|
-
--hash=sha256:
|
|
451
|
-
--hash=sha256:
|
|
452
|
-
--hash=sha256:
|
|
453
|
-
--hash=sha256:
|
|
454
|
-
--hash=sha256:
|
|
455
|
-
--hash=sha256:
|
|
456
|
-
--hash=sha256:
|
|
457
|
-
--hash=sha256:
|
|
458
|
-
--hash=sha256:
|
|
459
|
-
--hash=sha256:
|
|
460
|
-
--hash=sha256:
|
|
461
|
-
--hash=sha256:
|
|
462
|
-
--hash=sha256:
|
|
463
|
-
--hash=sha256:
|
|
464
|
-
--hash=sha256:
|
|
465
|
-
--hash=sha256:
|
|
466
|
-
--hash=sha256:
|
|
467
|
-
--hash=sha256:
|
|
468
|
-
--hash=sha256:
|
|
469
|
-
--hash=sha256:
|
|
470
|
-
--hash=sha256:
|
|
471
|
-
--hash=sha256:
|
|
472
|
-
--hash=sha256:
|
|
473
|
-
--hash=sha256:
|
|
474
|
-
--hash=sha256:
|
|
475
|
-
--hash=sha256:
|
|
476
|
-
--hash=sha256:
|
|
477
|
-
--hash=sha256:
|
|
478
|
-
--hash=sha256:
|
|
479
|
-
--hash=sha256:
|
|
480
|
-
--hash=sha256:
|
|
481
|
-
--hash=sha256:
|
|
482
|
-
--hash=sha256:
|
|
394
|
+
msgpack==1.2.3 \
|
|
395
|
+
--hash=sha256:07c9733089d1b176c3dd2f7fa268452f9d5d784d076473499d754a58e8d1fbbb \
|
|
396
|
+
--hash=sha256:0955b9000725573d1457c1676944b370dd9643c8d18f25bda5ac72913f850949 \
|
|
397
|
+
--hash=sha256:0c91762c48cd686dc9cf2b142c0bc544083952de32f5853d6624c956e54b85e5 \
|
|
398
|
+
--hash=sha256:0ed5823c4efc20fe87d3530665f40ec18a002be003114814c21235cc8d256207 \
|
|
399
|
+
--hash=sha256:13221a6c81ebb8e43ea63a7251c35d54e4175cea37ebf3a62e911bdf42562a3c \
|
|
400
|
+
--hash=sha256:186e6c602b8a9968b8e864c67d622a69279f7d1e55ae25f40e3bff7e815b2b62 \
|
|
401
|
+
--hash=sha256:18a6ed513023001b28dcd3ba54966f6bb90a38274ba8d2640464bcab3a1b81d4 \
|
|
402
|
+
--hash=sha256:1d6bcec3dbbdb89ca385d3a73e63ceae7b841fa0d7ca7c676f1a7bfe7fb2cdb8 \
|
|
403
|
+
--hash=sha256:1f4ae8bd4ad9ba085fde95e95d055a896d19210238a4199a771a3cf36dceed49 \
|
|
404
|
+
--hash=sha256:1f585407f740a9eac04a3bb82c61d68a0ea78f90e29e670bfb086b9ce3a518dd \
|
|
405
|
+
--hash=sha256:21bfa4d2aa0b04c1806ef778a1199e9e53ea2441bcbf284420a32083896320b8 \
|
|
406
|
+
--hash=sha256:2487453ca1b6104442c6442f9a1a8fee1fe8f428a70d99d4cba799108b304150 \
|
|
407
|
+
--hash=sha256:2574ef81c1c8c38b10e330f3f9406fd09198a776b002030fafcf8e7647e9e06e \
|
|
408
|
+
--hash=sha256:30e1522e4173230dca4d9ad896f038f73c0da6c1edd42f4dbad88ac583cf5d46 \
|
|
409
|
+
--hash=sha256:32edb81a2b5eb7cd7c9d941b2bfbbb082fd2cd09e0e725930316af6b708db186 \
|
|
410
|
+
--hash=sha256:3372475211a9ce1a23acefe512cb3e121d18c95dc74ed56cb1819ef40836ebf4 \
|
|
411
|
+
--hash=sha256:382b219de3d436de3baba0f4b0c6d4336e8f5858d0eb047918b13b69a71c6c55 \
|
|
412
|
+
--hash=sha256:382bc88fe90f29f5ac8a0b65c7046ff255356f2f2f3186c30e370215736fa1dc \
|
|
413
|
+
--hash=sha256:39b6986c19e1f2dfa549d185dba6ccf1de2e4c0ba10d8cfc0048935b1c5f9109 \
|
|
414
|
+
--hash=sha256:3a31905206722103a84c1f72633fe30692cff6732c9d262e09a27dbc468797c8 \
|
|
415
|
+
--hash=sha256:3d4c807ed050fe3ddbea5ba7e9f63d7136871ce42861be1f50ff739f0e91047a \
|
|
416
|
+
--hash=sha256:3ec409b0d6aa8e9eec6eaf881b893caa215dbe68c5319ca96e8a271d81bb111d \
|
|
417
|
+
--hash=sha256:471e12a6a42498a31490c206e0069e343b6a7c35db540be73a879eb06f5be047 \
|
|
418
|
+
--hash=sha256:4c0780095871ecc49a58b2ff6b1b43b25214704da67646557ca287a3f49fb2dd \
|
|
419
|
+
--hash=sha256:59612b4ed48a04cf024584218e813562f3b30a3bafa5f55abe300b15da314751 \
|
|
420
|
+
--hash=sha256:5bd5f91ea75c45cafcc5433ba8fae59b708b736ec178d2441c40c499e9e079db \
|
|
421
|
+
--hash=sha256:5bf390259cb25a6a1cd197c65810999b811f64cd38683251538bcc5a1e41f7d3 \
|
|
422
|
+
--hash=sha256:5c1efdd9181cb1b719ee46865f368a927f1c0c65d577798340b1194545b7515a \
|
|
423
|
+
--hash=sha256:5e0d7950ca3c1bbae291d0552dd3bb2792fc680629c4c0d44e47e5bab969f3ca \
|
|
424
|
+
--hash=sha256:5f304123b90e8b2e49867981b7f6061612c39f50cca51ee88de007c084cf68d3 \
|
|
425
|
+
--hash=sha256:62cc1a4ef0e553bac32c8342e1f04834aca7de276b92744eb7307db77759b890 \
|
|
426
|
+
--hash=sha256:63bb7448a1e9111319ae2430c09a5596140c160422830d6271bc75730ff2ff9a \
|
|
427
|
+
--hash=sha256:6576f348ed6cc4f31db6fd915a8e94245f042f50eae08d48732425e70638ea37 \
|
|
428
|
+
--hash=sha256:666ef5601ab0e6e345e47febc96aa81143cc932201543480cbb9499164f05ffb \
|
|
429
|
+
--hash=sha256:6707d2fa2aa1bb5424ea0b05f44ffc989b15ab41a73ff5855bff4944fec7c8ac \
|
|
430
|
+
--hash=sha256:69ad12cedb674c73527bed869cddb42b742cac79a207a614202a4abaa24ea173 \
|
|
431
|
+
--hash=sha256:6a834097144aabe948b8ca9020a833e8026f7d0abbd0ec54bc7e50f45a8ce012 \
|
|
432
|
+
--hash=sha256:6df430419f2338cb71e4a34d6e64f83c88ccd321f91f40ba4513400b36d864ec \
|
|
433
|
+
--hash=sha256:700bc0fc9e968a292b9137ee70e7a012f7e115bf0107ce45e3a88202788dfc1e \
|
|
434
|
+
--hash=sha256:7013534a7163aa4f213c4d9864f1a8a7555daac6fcd48f699a198e29b436bfab \
|
|
435
|
+
--hash=sha256:7995a7c6a62a1d6e7df211b4a16de513bd99fd053525050a319f80f44fb8015e \
|
|
436
|
+
--hash=sha256:79dfa38faf92f804aa61beec140d70b18418e1dde1778dbb77a87a4cce85aa8a \
|
|
437
|
+
--hash=sha256:7a003b02c6ee2eea6dfe0bb08818631e3597e69f0131f2a8250488a1cc553290 \
|
|
438
|
+
--hash=sha256:7c047250096f9fc19dba26e3d1639b5e7a84114003605c94def667149a70ced1 \
|
|
439
|
+
--hash=sha256:84a6616d396ec1bc18a1e83e67c96a393ec35dfe5e17434a5be7b9aa0fe988ab \
|
|
440
|
+
--hash=sha256:87cf2ef05ff2f2493ba29fcdaef27e960ca64dacfd13460ae29e6f92e0ed05bb \
|
|
441
|
+
--hash=sha256:89c930aece4e972b208ba589c8410b4167b05e411a5ea2cb25fd96f8bc47ee43 \
|
|
442
|
+
--hash=sha256:8ca67f77938ea6a3663aa9bd22b3e031f6da84d665be850abab910ee90728dfd \
|
|
443
|
+
--hash=sha256:8e51eca14fbb65c4e0a5a9657346962bd3dca78c08e04e3d4dee70ef48687d30 \
|
|
444
|
+
--hash=sha256:8ec7a1d49ca6c2569d722ab5ec86e90089b0713900aa31905b47b4c4d9e78ce0 \
|
|
445
|
+
--hash=sha256:902f3490db0e07a7d40b48536a85c9b28fbf1397e7e1658a45a55f958e303620 \
|
|
446
|
+
--hash=sha256:905a189853d6bdb204c7ae5f4ab77fb857448abfff574d3d93c62e2815b24b4f \
|
|
447
|
+
--hash=sha256:9276ba88891338f2617044429dfd080ae008c9868a25f6f1a7d004a35dc9ac0a \
|
|
448
|
+
--hash=sha256:9324c54995641c3d1f92a9d55093c8cde0ffa2fbc87a467a688ef60428393220 \
|
|
449
|
+
--hash=sha256:968583e956d0427878050b371308c5f8647088732ef3e66a117dbe1192ec91e0 \
|
|
450
|
+
--hash=sha256:9d7e9cbb0998bbfd363fd9a09c330520d5e9cb323c05b5a1a05865d23ccf2226 \
|
|
451
|
+
--hash=sha256:a393e428f6ffb0dcb73308c1fff5593041c16ff42da66e5bac8a83a6107a54b0 \
|
|
452
|
+
--hash=sha256:a6b63917d60d6df451f328bd6afba8565e33c4afe1f62ec4ad758b78731c827b \
|
|
453
|
+
--hash=sha256:b1631e12fe572e181cd77e831f69335d6cd5278eac22e3db3f33cf264ac2ac18 \
|
|
454
|
+
--hash=sha256:b774ff994d844e541439ac5d2d49a14def4104830c3465e9394c153f86200ffb \
|
|
455
|
+
--hash=sha256:b949cc25e4a09252cbcc54e66e507de914d0e94a3a7039bd54c299bf7037c098 \
|
|
456
|
+
--hash=sha256:bb89b5dc30469c84bbf8684826eb851d82412ca95690e111b9ac5e8fb343961a \
|
|
457
|
+
--hash=sha256:bfe7d5b62cbe7aa664f0b3e2c49077f10fcdd06183d3014f8271ff3c5edbfbf9 \
|
|
458
|
+
--hash=sha256:c309a7abae1d14ba29a8bd0ddbd704a5e469d8e9bd9c3dee0e4ff53d7ae01d56 \
|
|
459
|
+
--hash=sha256:c77e27790ad72989db783d5303825fba0b71550f00a490efba35cde7dc4b719f \
|
|
460
|
+
--hash=sha256:c942c21a93f36b3a69e828c8945bb72c94dc2ffe488a2086950c812f3edf046c \
|
|
461
|
+
--hash=sha256:ccea05b5542f6d283fef3f0a8e93a7f0be90af0ddeeef84c25c0216ba76dcae1 \
|
|
462
|
+
--hash=sha256:cd5a9f9f86a52c24713679aa2631956835f3842512964ff93f736ff76f1f530d \
|
|
463
|
+
--hash=sha256:d0238cd05dec9ffbe0de1071df685ba63e30a36ac155285b1a094e727c38cbe9 \
|
|
464
|
+
--hash=sha256:d1c1e8989a855b7f1f2a64ec4a80b23a631822903952770813857b2e4f460471 \
|
|
465
|
+
--hash=sha256:d2f9c4f85e47a44d26d5baf3b041eef23436e224d44eed273f01bd8a12048d9f \
|
|
466
|
+
--hash=sha256:d31864ba3933a589b6a00249f89c0eb422197f49128fc10da550e57e9cb0f377 \
|
|
467
|
+
--hash=sha256:d8ef3a66e4b52d2d7fdd90df2984670124b2ff7546d76bb25dcf68ef47f7df58 \
|
|
468
|
+
--hash=sha256:db84203b13aecc222f465061397fdd5b53b7ae73d2c95ffc1c8dc5be0153a709 \
|
|
469
|
+
--hash=sha256:db9fb67a3a2e75247bae569d34ebb5ff61c0448a4f0d6dbf991dae68af39b007 \
|
|
470
|
+
--hash=sha256:e0bd394e999949c814f7912284243298de1b5a17b6a3dcb6cc8a79b156ffc4fa \
|
|
471
|
+
--hash=sha256:e15f70588f4db8cd10df0930145b186de70feb9db51710cd378b1399009655bd \
|
|
472
|
+
--hash=sha256:e54394b7dbe2e12ab032d9d21feef7bb61a90a150a2623633ba3781ba69dcb1f \
|
|
473
|
+
--hash=sha256:eaf7e82249837e3aa97297b34a0bb9ff562027381631e057cea6e1367f10b438 \
|
|
474
|
+
--hash=sha256:ec0030361cc861ac699b2ef1c695b741fa145c88f8667fa3d7e3f73deeb648a3 \
|
|
475
|
+
--hash=sha256:ec90a9ae3e1169fa1171147340f0e97d941aa19fcd3b34e8339a55933ed042af \
|
|
476
|
+
--hash=sha256:ed899d73a22f286a72bd9528d63f2ab3030dbad8bf1527fc249319a50d61fb9d \
|
|
477
|
+
--hash=sha256:ede33b2892ceb976283e009ad12fa1834cfdf1f9c43ee9c97849fc588d00a618 \
|
|
478
|
+
--hash=sha256:f24a43b3560e20f825b807fe1e874bd73d53abaf8bbdcf258a6eb152cddbc1f5 \
|
|
479
|
+
--hash=sha256:f3d7b3d0018746b5997dd6b14a1870b07cc4c327d9101145d94a1fc264a51a06 \
|
|
480
|
+
--hash=sha256:f41ca154b7737b11893cdce3c78c61d703398a1cd54d4297bdad908392338a8e \
|
|
481
|
+
--hash=sha256:f42f146752eedb6765f07dcc04d72dab0a25779ec8d4a88c0085263ce114f22c \
|
|
482
|
+
--hash=sha256:f56fba61b2516be7917cb00151f0d060b5b21184e3499bb57f0f7d9259bea124 \
|
|
483
|
+
--hash=sha256:f9ddd28d3e9bbc602a9dced1591882c7fb9ab776eef8837da2c326fde19e2853 \
|
|
484
|
+
--hash=sha256:fafc3b8898b432b841d30a61082c599fa7f4d06885f9dc58ad72259e12059fa6 \
|
|
485
|
+
--hash=sha256:fcc6800daac4922960f6eeb7a0dda3dd4105e0bf7bce0e83ebc465a78cb7bdba
|
|
483
486
|
# via librosa
|
|
484
487
|
narwhals==2.26.0 \
|
|
485
488
|
--hash=sha256:29326d74f107c347fd1009bd58e38d9f7c7c5b51e6de97bc93dbc325d9038b54 \
|
|
@@ -3270,7 +3270,7 @@
|
|
|
3270
3270
|
},
|
|
3271
3271
|
{
|
|
3272
3272
|
"path": "templates/hyperframes/package-lock.json",
|
|
3273
|
-
"sha256": "
|
|
3273
|
+
"sha256": "115478d6f9ea0194666aa076559cd063955ba0d3fdc75782c2c8204e2a1c6599"
|
|
3274
3274
|
},
|
|
3275
3275
|
{
|
|
3276
3276
|
"path": "templates/hyperframes/package.json",
|
|
@@ -3282,11 +3282,11 @@
|
|
|
3282
3282
|
},
|
|
3283
3283
|
{
|
|
3284
3284
|
"path": "templates/remotion/package-lock.json",
|
|
3285
|
-
"sha256": "
|
|
3285
|
+
"sha256": "10beadf1d53049adcb83c75e8af35bb50d83907bb96d70b1deb1faad5fe8e6e6"
|
|
3286
3286
|
},
|
|
3287
3287
|
{
|
|
3288
3288
|
"path": "templates/remotion/package.json",
|
|
3289
|
-
"sha256": "
|
|
3289
|
+
"sha256": "84b1bd1a2624889e44dad6df00a79ac6d6ea51a3bda99050f7cebae8b588f5b9"
|
|
3290
3290
|
},
|
|
3291
3291
|
{
|
|
3292
3292
|
"path": "templates/remotion/src/index.tsx",
|
|
@@ -3789,7 +3789,7 @@
|
|
|
3789
3789
|
},
|
|
3790
3790
|
{
|
|
3791
3791
|
"path": "requirements-audio.txt",
|
|
3792
|
-
"sha256": "
|
|
3792
|
+
"sha256": "55bc85467f4e416db2d6069d23e94f8ad7f3b515432a166797c4a682e4451503"
|
|
3793
3793
|
},
|
|
3794
3794
|
{
|
|
3795
3795
|
"path": "runtime.mjs",
|
|
@@ -463,16 +463,16 @@
|
|
|
463
463
|
}
|
|
464
464
|
},
|
|
465
465
|
"node_modules/@hono/node-server": {
|
|
466
|
-
"version": "2.1.
|
|
467
|
-
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.
|
|
468
|
-
"integrity": "sha512-
|
|
466
|
+
"version": "2.1.5",
|
|
467
|
+
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz",
|
|
468
|
+
"integrity": "sha512-MgP/I06GdvsilDspMk/qmRzpk8LOUStP6jFAbSRi40W09DhoyUn+Mj7eyKICPqeYKybOjVFLVIAQzvu1bfIa7Q==",
|
|
469
469
|
"dev": true,
|
|
470
470
|
"license": "MIT",
|
|
471
471
|
"engines": {
|
|
472
472
|
"node": ">=20"
|
|
473
473
|
},
|
|
474
474
|
"peerDependencies": {
|
|
475
|
-
"hono": "^4"
|
|
475
|
+
"hono": "^4 || ^5.0.0-0"
|
|
476
476
|
}
|
|
477
477
|
},
|
|
478
478
|
"node_modules/@img/colour": {
|
|
@@ -3771,9 +3771,9 @@
|
|
|
3771
3771
|
"license": "ISC"
|
|
3772
3772
|
},
|
|
3773
3773
|
"node_modules/zod": {
|
|
3774
|
-
"version": "4.
|
|
3775
|
-
"resolved": "https://registry.npmjs.org/zod/-/zod-4.
|
|
3776
|
-
"integrity": "sha512-
|
|
3774
|
+
"version": "4.6.6",
|
|
3775
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-4.6.6.tgz",
|
|
3776
|
+
"integrity": "sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==",
|
|
3777
3777
|
"license": "MIT",
|
|
3778
3778
|
"funding": {
|
|
3779
3779
|
"url": "https://github.com/sponsors/colinhacks"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"readme-studio-motion","private":true,"type":"module","scripts":{"compositions":"remotion compositions src/index.tsx"},"dependencies":{"@remotion/cli":"4.0.526","remotion":"4.0.526","react":"19.2.0","react-dom":"19.2.0"}}
|
|
1
|
+
{"name":"readme-studio-motion","private":true,"type":"module","scripts":{"compositions":"remotion compositions src/index.tsx"},"dependencies":{"@remotion/cli":"4.0.526","remotion":"4.0.526","react":"19.2.0","react-dom":"19.2.0"},"overrides":{"zod":"4.6.6"}}
|