@lawaty/lacode 1.3.0-beta.1 → 1.3.0-beta.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/tui.js +76 -6
  3. package/package.json +12 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,50 @@ All notable changes to LaCode are recorded here. The format follows
7
7
  Versions come from git release commits; the npm version is bumped in the same
8
8
  commit as the changelog entry it describes.
9
9
 
10
+ ## [1.3.0-beta.3] — the 1.3.0-beta.2 notes
11
+
12
+ ### Changed
13
+
14
+ - **Republished so the published tarball carries the 1.3.0-beta.2 changelog
15
+ entry.** No runtime change: the code is identical to 1.3.0-beta.2.
16
+
17
+ ## [1.3.0-beta.2] — a reason that survives, and a timeline that ships
18
+
19
+ ### Added
20
+
21
+ - **A durable failure journal.** Every failed or cancelled spawn appends one JSON
22
+ line to `failures.jsonl` in the pool state directory: timestamp, source, base,
23
+ slot, model, reason, elapsed time and the error text. `pool_status` prints the
24
+ last five under `recent failures:`, so a slot that dies without a cooldown is
25
+ no longer invisible afterwards.
26
+
27
+ ### Changed
28
+
29
+ - **A spawn the pool did not abort now records why it died.** An unclassified
30
+ `session.error`, or an assistant message error matching no known class, used to
31
+ release the claim and say nothing. It now records the slot, model and error
32
+ text, and the cancelled task card is rewritten with that reason. A stop the
33
+ user presses still produces no record and no rewrite, because it raises no
34
+ session error.
35
+
36
+ ### Fixed
37
+
38
+ - **The cancelled-task rewrite was unauthenticated.** It hand-rolled a `fetch`
39
+ PATCH and read `OPENCODE_SERVER_PASSWORD` from the plugin's own environment —
40
+ but that variable exists only in the server process, so the request always went
41
+ out unauthenticated and a password-protected server answered `401`. The reason
42
+ was then dropped and the caller saw a bare `Task cancelled`. The rewrite now
43
+ goes through the injected client's part update, which is already authenticated;
44
+ the raw request remains only as a fallback for a runtime without it.
45
+ - **The timeline sidebar was never bundled.** `src/tui/timeline.tsx` was added in
46
+ 1.2.0 but never imported by the TUI entry point, so `dist/tui.js` contained no
47
+ reference to it and no published version shipped the panel: it worked only on a
48
+ machine whose `tui.json` pointed at the raw source file. The TUI entry now
49
+ composes it, `solid-js` and `@opentui/solid` are treated as host-provided peers
50
+ instead of being bundled, and the automatic JSX transform is enabled so the
51
+ `@opentui/solid` pragma is honoured rather than silently compiling to
52
+ `React.createElement`.
53
+
10
54
  ## [1.3.0-beta.0] — provider readiness, timeline roots, and a reason on cancelled tasks
11
55
 
12
56
  ### Added
package/dist/tui.js CHANGED
@@ -85,17 +85,17 @@ function createRecorder(deps) {
85
85
  const child = childHandle;
86
86
  const filePath = currentPath;
87
87
  if (!exited) {
88
- await new Promise((resolve) => {
88
+ await new Promise((resolve2) => {
89
89
  let done = false;
90
90
  let timer;
91
91
  const finish = () => {
92
92
  if (done) return;
93
93
  done = true;
94
94
  if (timer) clearTimeout(timer);
95
- if (resolveWait === resolve) resolveWait = null;
96
- resolve();
95
+ if (resolveWait === resolve2) resolveWait = null;
96
+ resolve2();
97
97
  };
98
- resolveWait = resolve;
98
+ resolveWait = resolve2;
99
99
  timer = setTimeout(() => {
100
100
  try {
101
101
  child.kill("SIGKILL");
@@ -821,6 +821,68 @@ function createVoiceClient(options, deps) {
821
821
  return client;
822
822
  }
823
823
 
824
+ // src/tui/timeline.tsx
825
+ import { createSignal, createEffect, onCleanup, For } from "solid-js";
826
+ import { readFileSync as readFileSync3 } from "node:fs";
827
+ import { homedir as homedir2 } from "node:os";
828
+ import { join as join3, resolve } from "node:path";
829
+ import { jsx, jsxs } from "@opentui/solid/jsx-runtime";
830
+ var registered = false;
831
+ var tui = async (api) => {
832
+ if (registered) return;
833
+ registered = true;
834
+ api.slots.register({
835
+ order: 150,
836
+ slots: {
837
+ sidebar_content(ctx, props) {
838
+ const [entries, setEntries] = createSignal([]);
839
+ const read = (id) => {
840
+ if (typeof id !== "string" || !/^[A-Za-z0-9_.-]+$/.test(id)) {
841
+ setEntries([]);
842
+ return;
843
+ }
844
+ const rawDir = process.env.LACODE_TIMELINE_DIR;
845
+ const dir = rawDir ? resolve(rawDir) : join3(homedir2(), ".local/share/opencode/lacode/timeline");
846
+ const file = join3(dir, id + ".jsonl");
847
+ try {
848
+ const raw = readFileSync3(file, "utf8");
849
+ const parsed = [];
850
+ for (const line of raw.split("\n")) {
851
+ const trimmed = line.trim();
852
+ if (!trimmed) continue;
853
+ try {
854
+ const obj = JSON.parse(trimmed);
855
+ if (obj && typeof obj.ts === "number" && typeof obj.text === "string") {
856
+ parsed.push(obj.text);
857
+ }
858
+ } catch {
859
+ }
860
+ }
861
+ setEntries(parsed.slice(-14).reverse());
862
+ } catch {
863
+ setEntries([]);
864
+ }
865
+ };
866
+ createEffect(() => {
867
+ const id = props.session_id;
868
+ read(id);
869
+ const timer = setInterval(() => read(id), 1e3);
870
+ onCleanup(() => clearInterval(timer));
871
+ });
872
+ return /* @__PURE__ */ jsxs("box", { flexDirection: "column", children: [
873
+ /* @__PURE__ */ jsx("text", { fg: ctx.theme.current.textMuted, children: "Timeline" }),
874
+ entries().length === 0 ? /* @__PURE__ */ jsx("text", { fg: ctx.theme.current.textMuted, children: "No timeline yet" }) : /* @__PURE__ */ jsx(For, { each: entries(), children: (text) => /* @__PURE__ */ jsx("text", { children: `\u2022 ${text}` }) })
875
+ ] });
876
+ }
877
+ }
878
+ });
879
+ };
880
+ var plugin = {
881
+ id: "lacode.timeline",
882
+ tui
883
+ };
884
+ var timeline_default = plugin;
885
+
824
886
  // src/tui/index.ts
825
887
  var DEFAULT_MODEL = "whisper-large-v3-turbo";
826
888
  var DEFAULT_LANGUAGE = "en";
@@ -1049,7 +1111,7 @@ async function answerWithText(api, opts, text, request) {
1049
1111
  const labels = answers.map((answer) => answer.join(", ")).filter((answer) => answer.length > 0).join("; ");
1050
1112
  toast(api, { variant: "success", message: labels });
1051
1113
  }
1052
- var tui = async (api, options) => {
1114
+ var tui2 = async (api, options) => {
1053
1115
  const opts = resolveOptions(options);
1054
1116
  if (!opts.enabled) return;
1055
1117
  daemonConnected = false;
@@ -1271,10 +1333,18 @@ var tui = async (api, options) => {
1271
1333
  bindings.push({ key: opts.answerKey, cmd: "lacode.voice.answer", desc: "Voice answer" });
1272
1334
  }
1273
1335
  api.keymap.registerLayer({ mode: "base", commands, bindings });
1336
+ const timelineOpts = options ?? {};
1337
+ const timelineOff = timelineOpts.timeline === false || typeof timelineOpts.timeline === "object" && timelineOpts.timeline !== null && timelineOpts.timeline.enabled === false;
1338
+ if (!timelineOff) {
1339
+ try {
1340
+ await timeline_default.tui(api, options);
1341
+ } catch {
1342
+ }
1343
+ }
1274
1344
  };
1275
1345
  var mod = {
1276
1346
  id: "lacode.voice",
1277
- tui
1347
+ tui: tui2
1278
1348
  };
1279
1349
  var index_default = mod;
1280
1350
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawaty/lacode",
3
- "version": "1.3.0-beta.1",
3
+ "version": "1.3.0-beta.3",
4
4
  "description": "Ultimate opencode setup for Go users — cost-controlled pooled subagents, hang recovery, and notifications for opencode.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -64,7 +64,17 @@
64
64
  "esbuild": "^0.28.2"
65
65
  },
66
66
  "peerDependencies": {
67
- "@opencode-ai/plugin": "1.18.34"
67
+ "@opencode-ai/plugin": "1.18.34",
68
+ "solid-js": "*",
69
+ "@opentui/solid": "*"
70
+ },
71
+ "peerDependenciesMeta": {
72
+ "solid-js": {
73
+ "optional": true
74
+ },
75
+ "@opentui/solid": {
76
+ "optional": true
77
+ }
68
78
  },
69
79
  "optionalDependencies": {
70
80
  "@picovoice/porcupine-node": "^4.0.2",