my-durable 0.0.34 → 0.0.36

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.
@@ -7,8 +7,8 @@
7
7
  <meta name="referrer" content="no-referrer" />
8
8
  <link rel="icon" type="image/svg+xml" href="/icons/durable-pi.svg" />
9
9
  <title>Durable Pi | Network</title>
10
- <script type="module" crossorigin src="/assets/index-DqUQknw8.js"></script>
11
- <link rel="stylesheet" crossorigin href="/assets/index-D6aUyqqF.css">
10
+ <script type="module" crossorigin src="/assets/index-DKJspajj.js"></script>
11
+ <link rel="stylesheet" crossorigin href="/assets/index-BxJGBCW9.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="app"></div>
@@ -2,9 +2,10 @@
2
2
  // something actually looks at it, kept across tab switches, never re-fetched
3
3
  // while fresh. In-flight calls are shared so chip + panel + re-entry of either
4
4
  // cost ONE device round-trip.
5
- import { reactive } from "vue";
5
+ import { reactive, watch } from "vue";
6
6
  import type { GitStatus } from "@sdk/client";
7
7
  import { deviceClient } from "./hub";
8
+ import { network } from "./store";
8
9
 
9
10
  export interface GitEntry {
10
11
  at: number; // last settled fetch (0 = never)
@@ -37,6 +38,32 @@ export async function ensureGitBatch(deviceId: string, paths: string[]) {
37
38
  }
38
39
  const staleMsForBatch = 60_000;
39
40
 
41
+ /** fs-auto-refresh, FE side: the device's tree carries dirty marks for repos
42
+ * its watcher saw change — refresh exactly those entries, still lazy (never
43
+ * an interval), throttled per entry. */
44
+ let wired = false;
45
+ export function wireGitAutoRefresh() {
46
+ if (wired || import.meta.env.SSR) return;
47
+ wired = true;
48
+ watch(() => network.devices.value, (devices) => {
49
+ const now = Date.now();
50
+ for (const d of devices) {
51
+ const marks = ((d as { tree?: { git?: { root: string; at: number }[] } }).tree?.git ?? []).filter(
52
+ (m) => now - m.at < 60_000,
53
+ );
54
+ if (!marks.length) continue;
55
+ for (const key of [...entries.keys()]) {
56
+ const [devId, cwd] = key.split("::");
57
+ if (devId !== d.id || !cwd) continue;
58
+ const e = entries.get(key)!;
59
+ if (!marks.some((m) => cwd === m.root || cwd.startsWith(m.root + "/") || cwd.startsWith(m.root + "\\"))) continue;
60
+ if (e.loading || now - e.at < 4000) continue; // throttle: bursts coalesce
61
+ void ensureGit(devId, cwd, { force: true });
62
+ }
63
+ }
64
+ });
65
+ }
66
+
40
67
  /** Test hook: the store is module-global on purpose (tab switches must not
41
68
  * refetch) — tests reset it so entries never leak between cases. */
42
69
  export function clearGitCache() {
@@ -34,7 +34,7 @@ import {
34
34
  } from "lucide-vue-next";
35
35
  import { deviceId, network, sessionSummary, notify, select } from "../client/store";
36
36
  import { deviceClient } from "../client/hub";
37
- import { ensureGit, gitEntry } from "../client/git-store";
37
+ import { ensureGit, gitEntry, wireGitAutoRefresh } from "../client/git-store";
38
38
  import type { GitStatus } from "@sdk/client";
39
39
 
40
40
  const props = defineProps<{ deviceId?: string; cwd?: string }>();
@@ -201,7 +201,7 @@ async function prCommentNow(n: number, text: string) {
201
201
  }
202
202
 
203
203
  /** Per-turn summaries (zcode taskChangeSummary) under the session chip. */
204
- const turns = ref<{ when: string; files: { path: string; writes: number }[] }[]>([]);
204
+ const turns = ref<{ when: string; files: { path: string; writes: number; a?: number; d?: number }[] }[]>([]);
205
205
  const turnsOpen = ref(false);
206
206
  const loadTurns = async () => {
207
207
  if (!devId.value || !sessionId.value) return;
@@ -233,6 +233,7 @@ const draftLoad = () => {
233
233
  commitMsg.value = localStorage.getItem(draftKey.value) ?? "";
234
234
  };
235
235
  if (!import.meta.env.SSR) {
236
+ wireGitAutoRefresh();
236
237
  void ensureGit(devId.value || "?", cwd.value || "?");
237
238
  draftLoad();
238
239
  }
@@ -535,9 +536,11 @@ async function commit(thenPush = false) {
535
536
  if (!chosen.length) return notify("check the files to commit — nothing is selected");
536
537
  const client = deviceClient(devId.value);
537
538
  try {
539
+ // During a merge git refuses path-limited commits ("cannot do a partial
540
+ // commit during a merge") — the changelist is everything staged anyway.
538
541
  const r = await client.gitOp(cwd.value, "commit", {
539
542
  message: commitMsg.value.trim(),
540
- files: chosen,
543
+ ...(status.value?.merge ? {} : { files: chosen }),
541
544
  });
542
545
  notify(r.text);
543
546
  commitMsg.value = "";
@@ -959,7 +962,7 @@ const laneX = (lane: number) => lane * 12 + 7;
959
962
  <div v-for="(t, ti) in turns" :key="ti" class="cp-row">
960
963
  <code>t{{ turns.length - ti }}</code>
961
964
  <span class="muted-line">{{ t.when.slice(0, 16).replace("T", " ") }}</span>
962
- <span class="cp-subj">{{ t.files.map((f) => `${f.path}×${f.writes}`).join(", ") || "(no file writes)" }}</span>
965
+ <span class="cp-subj">{{ t.files.map((f) => `${f.path}×${f.writes}${f.a !== undefined || f.d !== undefined ? ` (+${f.a ?? 0} −${f.d ?? 0})` : ""}`).join(", ") || "(no file writes)" }}</span>
963
966
  </div>
964
967
  </div>
965
968
  <p v-if="atCap" class="muted-line cap">showing the first 500 changed files — commit in smaller batches</p>
@@ -194,6 +194,9 @@ export interface DeviceTree {
194
194
  name: string;
195
195
  sessions: SessionSummary[];
196
196
  at: number;
197
+ /** Repos that changed on-device recently (fs watcher) — the cue for
198
+ * connected UIs to refresh their cached git status. */
199
+ git?: { root: string; at: number }[];
197
200
  }
198
201
 
199
202
  export interface AgentConfig {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-durable",
3
- "version": "0.0.34",
3
+ "version": "0.0.36",
4
4
  "description": "Durable Pi \u2014 the durable agent network: hub web UI, device backend daemon, TUI admin and the shared SDK, packaged as one runnable monorepo install",
5
5
  "license": "MIT",
6
6
  "repository": {