ponzu 1.71.0 → 1.71.2

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/dist/index.js CHANGED
@@ -6978,7 +6978,7 @@ var require_dist = __commonJS(function(exports, module) {
6978
6978
  // apps/cli/src/index.ts
6979
6979
  import { existsSync as existsSync13 } from "fs";
6980
6980
  import { networkInterfaces } from "os";
6981
- import { dirname as dirname12, isAbsolute as isAbsolute4, join as join20, resolve as resolve5 } from "path";
6981
+ import { dirname as dirname13, isAbsolute as isAbsolute4, join as join21, resolve as resolve5 } from "path";
6982
6982
  import { fileURLToPath as fileURLToPath2 } from "url";
6983
6983
  import { parseArgs } from "util";
6984
6984
 
@@ -33328,7 +33328,7 @@ When to watch: whenever the person might react to the work on the canvas. That's
33328
33328
 
33329
33329
  Watching doesn't cut you off from chat. A message the person types reaches you as soon as the current wait returns, within a minute. To tell them something (here's the link, which option you'd pick), write it in chat, then keep watching.
33330
33330
 
33331
- 1. listen: wait_for_activity with the cursor from your last call. Start with types comment.created, comment.replied, comment.edited, file.changed. An edit replaces what the comment asks; work from the new text. If the wait says Ponzu restarted or lost track, it lists the open threads waiting on you instead: work through those, then carry on with the cursor it gives. The wait hears from every file you've worked in this session and names the file for any event outside the one you passed: pass that file when you act on it.
33331
+ 1. listen: wait_for_activity with the cursor from your last call. Start with types comment.created, comment.replied, comment.edited, file.changed. An edit replaces what the comment asks; work from the new text. If the wait says Ponzu restarted or lost track, it lists the open threads waiting on you instead: work through those, then carry on with the cursor it gives. If it says Ponzu stopped and isn't back yet, nothing was checked: wait again with the cursor it gives, as Ponzu is most likely restarting into an update. The wait hears from every file you've worked in this session and names the file for any event outside the one you passed: pass that file when you act on it.
33332
33332
  2. triage each comment:
33333
33333
  - change ("make this bigger", "try blue"): do it.
33334
33334
  - question ("why this font?"): reply with an answer, leave it open.
@@ -33422,6 +33422,7 @@ See it
33422
33422
 
33423
33423
  What the person sees
33424
33424
  - On their canvas, the load plays once when the artboard appears: when they open its page, and each time you edit it. Then a finite animation holds its end. Hovering or clicking the artboard doesn't replay it, so never tell them it will.
33425
+ - Their canvas plays what record shows. It holds motion still on purpose: while its window is in the background on battery, while they pan across a crowded page, for parts too small to see at their zoom, and for loops longer than 20 s on an artboard zoomed far out. If record shows the motion working and they say it doesn't play, tell the Ponzu team with send_feedback instead of reworking the design.
33425
33426
  - A hint that fades out ends hidden, so the canvas shows nothing once it has played.
33426
33427
  - When they need to watch the motion (an entrance to review, a hint that leaves), put a looping copy on an artboard beside the real one: animation-iteration-count: infinite, with a beat of about a second held at the end of the keyframes so each cycle reads as one play. Keep the real one finite, and say which is which in a note.
33427
33428
  - If the motion answers something the person does, put it on :hover or :focus, so they can set it off themselves.
@@ -33541,7 +33542,7 @@ var listenTools = [
33541
33542
  title: "Wait for activity",
33542
33543
  group: "listen",
33543
33544
  readOnly: true,
33544
- description: "Watch the canvas. Call this instead of ending your turn whenever the person might react to your work there: after you put something up for review, or while you design together. Calling it again and again is the loop (get_guide('watch')). Long-polls for what people do in the canvas: new comments and replies, edits made in a text editor, other agents' changes, in this file and the others you've worked in this session, saying which file each is in. Returns immediately if anything happened after `since`; otherwise waits up to `timeout` seconds. Pass back the returned cursor next time so nothing is missed. If Ponzu restarted or lost track since then, it returns at once with the open threads waiting on you. The person's chat messages still reach you between waits.",
33545
+ description: "Watch the canvas. Call this instead of ending your turn whenever the person might react to your work there: after you put something up for review, or while you design together. Calling it again and again is the loop (get_guide('watch')). Long-polls for what people do in the canvas: new comments and replies, edits made in a text editor, other agents' changes, in this file and the others you've worked in this session, saying which file each is in. Returns immediately if anything happened after `since`; otherwise waits up to `timeout` seconds. Pass back the returned cursor next time so nothing is missed. If Ponzu restarts during the wait, the wait carries on once it's back. If it restarted or lost track before you waited, it returns at once with the open threads waiting on you: handle those, then keep watching with the cursor it gives. The person's chat messages still reach you between waits.",
33545
33546
  input: {
33546
33547
  file: fileArg,
33547
33548
  since: number2().int().min(0).optional().describe("Cursor from the last call. Omit to start from now."),
@@ -33570,7 +33571,7 @@ var listenTools = [
33570
33571
  const waiting = waitingText(files, all);
33571
33572
  return {
33572
33573
  text: `${GAP_NOTE[gap]} ${waiting.text}
33573
- Wait again with cursor=${next}.`,
33574
+ Keep watching: wait again with cursor=${next}.`,
33574
33575
  data: { cursor: next, events: [], gap, waiting: waiting.ids },
33575
33576
  metrics: { cursor: gap, events: 0, scope_files: files.length }
33576
33577
  };
@@ -36772,6 +36773,7 @@ data:
36772
36773
 
36773
36774
  // packages/mcp/src/index.ts
36774
36775
  var FILES_META = "ponzu.design/files";
36776
+ var WAIT_META = "ponzu.design/wait";
36775
36777
  function clientKind(name = "") {
36776
36778
  const n = name.toLowerCase();
36777
36779
  if (n.includes("claude"))
@@ -36840,7 +36842,7 @@ function createMcpServer(ws, opts = {}) {
36840
36842
  destructiveHint: def.destructive ?? false,
36841
36843
  openWorldHint: false
36842
36844
  }
36843
- }, async (args, extra) => toMcp(await runTool(def, args, { ws, caller: getCaller(), signal: extra.signal, session }), session));
36845
+ }, async (args, extra) => toMcp(await runTool(def, args, { ws, caller: getCaller(), signal: extra.signal, session }), session, def.name));
36844
36846
  }
36845
36847
  for (const [topic, text] of Object.entries(GUIDES)) {
36846
36848
  server.registerResource(`guide-${topic}`, `ponzu://guide/${topic}`, { title: `Ponzu guide: ${topic}`, mimeType: "text/markdown" }, async (uri) => ({ contents: [{ uri: uri.href, mimeType: "text/markdown", text }] }));
@@ -36879,13 +36881,19 @@ Read get_guide('basics'). Write the brief and your direction to a note, set toke
36879
36881
  }));
36880
36882
  return server;
36881
36883
  }
36882
- function toMcp(r, session) {
36884
+ function toMcp(r, session, tool) {
36883
36885
  const content = [];
36884
36886
  for (const img of r.images ?? [])
36885
36887
  content.push({ type: "image", data: img.data, mimeType: img.mime });
36886
36888
  content.push({ type: "text", text: r.text });
36887
- const files = session.files?.length ? { _meta: { [FILES_META]: session.files } } : {};
36888
- return { content, isError: r.isError, ...files };
36889
+ const meta = {};
36890
+ if (session.files?.length)
36891
+ meta[FILES_META] = session.files;
36892
+ if (tool === "wait_for_activity" && r.data) {
36893
+ const { cursor, gap, waiting } = r.data;
36894
+ meta[WAIT_META] = { cursor, ...gap ? { gap, waiting: waiting?.length ?? 0 } : {} };
36895
+ }
36896
+ return { content, isError: r.isError, ...Object.keys(meta).length ? { _meta: meta } : {} };
36889
36897
  }
36890
36898
  function createHttpHandler(ws, opts = {}) {
36891
36899
  const sessions = new Map;
@@ -37084,6 +37092,7 @@ function onBodyDone(res, done) {
37084
37092
 
37085
37093
  // packages/mcp/src/bridge.ts
37086
37094
  var CALL_TIMEOUT = 15 * 60000;
37095
+ var RESTART_GRACE = 1e4;
37087
37096
  var readOnly = new Set(tools.filter((t) => t.readOnly).map((t) => t.name));
37088
37097
  async function serveBridge(opts, transport = new StdioServerTransport) {
37089
37098
  const server = new Server({ name: opts.name ?? "ponzu", version: opts.version ?? "0.1.0", title: "Ponzu" }, {
@@ -37213,19 +37222,70 @@ async function serveBridge(opts, transport = new StdioServerTransport) {
37213
37222
  return fn(await localClient(), extra.signal);
37214
37223
  }
37215
37224
  };
37216
- server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
37217
- const name = req.params.name;
37218
- const args = req.params.arguments ?? {};
37219
- const params = { ...req.params, arguments: opts.mapArgs ? opts.mapArgs(name, args) : args };
37220
- const options = { signal: extra.signal, timeout: CALL_TIMEOUT };
37225
+ const hostBack = async (until, signal) => {
37226
+ const giveUp = Math.min(until, Date.now() + (opts.restartGrace ?? RESTART_GRACE));
37227
+ while (!signal.aborted) {
37228
+ if (await opts.findHost(false).catch(() => null))
37229
+ return true;
37230
+ if (Date.now() >= giveUp)
37231
+ return false;
37232
+ await new Promise((r) => setTimeout(r, 250));
37233
+ }
37234
+ return false;
37235
+ };
37236
+ const waitThrough = async (params, signal) => {
37237
+ const { since, timeout } = params.arguments ?? {};
37238
+ const deadline = Date.now() + (typeof timeout === "number" ? timeout : 60) * 1000;
37239
+ const left = () => Math.max(0, Math.ceil((deadline - Date.now()) / 1000));
37240
+ const withArgs = (more) => ({
37241
+ ...params,
37242
+ arguments: { ...params.arguments, ...more }
37243
+ });
37244
+ let cutOff = false;
37245
+ const result = await call(params, signal, async () => {
37246
+ cutOff = true;
37247
+ if (!await hostBack(deadline, signal) && Date.now() >= deadline)
37248
+ return null;
37249
+ return withArgs({ timeout: left() });
37250
+ });
37251
+ if (!result) {
37252
+ const again = typeof since === "number" ? `Wait again with cursor=${since}` : "Wait again";
37253
+ return {
37254
+ content: [
37255
+ {
37256
+ type: "text",
37257
+ text: `Ponzu stopped while you waited and isn't back yet, so nothing was checked. ${again}: it's most likely restarting into an update.`
37258
+ }
37259
+ ]
37260
+ };
37261
+ }
37262
+ const wait = result._meta?.[WAIT_META];
37263
+ if (!cutOff || wait?.gap !== "restart" || wait.waiting || !left())
37264
+ return result;
37265
+ const again = await call(withArgs({ since: wait.cursor, timeout: left() }), signal);
37266
+ const first = again.content.findIndex((c) => c.type === "text");
37267
+ return {
37268
+ ...again,
37269
+ content: again.content.map((c, i) => i === first && c.type === "text" ? {
37270
+ ...c,
37271
+ text: `Ponzu restarted while you waited, with nothing waiting on you.
37272
+ ${c.text}`
37273
+ } : c)
37274
+ };
37275
+ };
37276
+ const call = async (params, signal, cutOff) => {
37277
+ const name = params.name;
37278
+ let sent = params;
37279
+ let ridden = cutOff;
37280
+ const options = { signal, timeout: CALL_TIMEOUT };
37221
37281
  for (let attempt = 0;attempt < 2; attempt++) {
37222
37282
  const client = await hostClient();
37223
37283
  if (!client)
37224
37284
  break;
37225
37285
  try {
37226
- return remember(await using(client, () => client.request({ method: "tools/call", params }, CallToolResultSchema, options)));
37286
+ return remember(await using(client, () => client.request({ method: "tools/call", params: sent }, CallToolResultSchema, options)));
37227
37287
  } catch (err) {
37228
- if (extra.signal.aborted || err instanceof McpError && err.code !== ErrorCode.ConnectionClosed)
37288
+ if (signal.aborted || err instanceof McpError && err.code !== ErrorCode.ConnectionClosed)
37229
37289
  throw err;
37230
37290
  await dropHost(client);
37231
37291
  if (!neverArrived(err) && !readOnly.has(name)) {
@@ -37239,10 +37299,27 @@ async function serveBridge(opts, transport = new StdioServerTransport) {
37239
37299
  isError: true
37240
37300
  };
37241
37301
  }
37302
+ if (ridden && !neverArrived(err)) {
37303
+ const next = await ridden();
37304
+ if (!next)
37305
+ return null;
37306
+ sent = next;
37307
+ ridden = undefined;
37308
+ }
37242
37309
  }
37243
37310
  }
37311
+ if (sent !== params)
37312
+ return null;
37244
37313
  await opts.prepareLocal?.();
37245
- return remember(await (await localClient()).request({ method: "tools/call", params }, CallToolResultSchema, options));
37314
+ return remember(await (await localClient()).request({ method: "tools/call", params: sent }, CallToolResultSchema, options));
37315
+ };
37316
+ server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
37317
+ const name = req.params.name;
37318
+ const args = req.params.arguments ?? {};
37319
+ const params = { ...req.params, arguments: opts.mapArgs ? opts.mapArgs(name, args) : args };
37320
+ if (name === "wait_for_activity")
37321
+ return waitThrough(params, extra.signal);
37322
+ return await call(params, extra.signal);
37246
37323
  });
37247
37324
  server.setRequestHandler(ListToolsRequestSchema, (req, extra) => list((c, signal) => c.listTools(req.params, { signal }))(req, extra));
37248
37325
  server.setRequestHandler(ListResourcesRequestSchema, (req, extra) => list((c, signal) => c.listResources(req.params, { signal }))(req, extra));
@@ -47639,7 +47716,7 @@ function remoteUpdater(endpoint, token) {
47639
47716
  // apps/cli/package.json
47640
47717
  var package_default = {
47641
47718
  name: "ponzu",
47642
- version: "1.71.0",
47719
+ version: "1.71.2",
47643
47720
  description: "The open source canvas your agent draws on in HTML and CSS.",
47644
47721
  type: "module",
47645
47722
  license: "MIT",
@@ -47662,17 +47739,40 @@ var package_default = {
47662
47739
 
47663
47740
  // apps/cli/src/host.ts
47664
47741
  import { spawn as spawn6 } from "child_process";
47665
- import { existsSync as existsSync9, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
47742
+ import { existsSync as existsSync9, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
47666
47743
  import { chmod, copyFile, mkdir as mkdir11, rename as rename9, rm as rm5, utimes as utimes3, writeFile as writeFile9 } from "fs/promises";
47744
+ import { homedir as homedir6 } from "os";
47745
+ import { dirname as dirname7, join as join15, posix, win32 } from "path";
47746
+
47747
+ // packages/server/src/updating.ts
47748
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync2, rmSync, writeFileSync } from "fs";
47667
47749
  import { homedir as homedir5 } from "os";
47668
- import { dirname as dirname6, join as join14, posix, win32 } from "path";
47669
- var ponzuHome = () => process.env.PONZU_HOME ?? join14(homedir5(), ".ponzu");
47670
- var hostFile = () => join14(ponzuHome(), "host.json");
47750
+ import { dirname as dirname6, join as join14 } from "path";
47751
+ var UPDATING_WITHIN = 2 * 60000;
47752
+ var updatingFile = () => join14(process.env.PONZU_HOME ?? join14(homedir5(), ".ponzu"), "updating.json");
47753
+ function read() {
47754
+ try {
47755
+ const u = JSON.parse(readFileSync2(updatingFile(), "utf8"));
47756
+ if (typeof u.from !== "string" || typeof u.to !== "string" || typeof u.at !== "number")
47757
+ return null;
47758
+ return { from: u.from, to: u.to, at: u.at, ...u.told ? { told: true } : {} };
47759
+ } catch {
47760
+ return null;
47761
+ }
47762
+ }
47763
+ function updating(now = Date.now()) {
47764
+ const u = read();
47765
+ return u && now - u.at < UPDATING_WITHIN ? u : null;
47766
+ }
47767
+
47768
+ // apps/cli/src/host.ts
47769
+ var ponzuHome = () => process.env.PONZU_HOME ?? join15(homedir6(), ".ponzu");
47770
+ var hostFile = () => join15(ponzuHome(), "host.json");
47671
47771
  async function announceHost(url, opts = {}) {
47672
47772
  if (!opts.takeOver && await runningHost())
47673
47773
  return async () => {};
47674
47774
  const path = hostFile();
47675
- await mkdir11(dirname6(path), { recursive: true });
47775
+ await mkdir11(dirname7(path), { recursive: true });
47676
47776
  const me = { url, mcp: `${url}/mcp`, pid: process.pid };
47677
47777
  await writeFile9(path, `${JSON.stringify(me)}
47678
47778
  `);
@@ -47683,7 +47783,7 @@ async function announceHost(url, opts = {}) {
47683
47783
  }
47684
47784
  function readHost() {
47685
47785
  try {
47686
- return JSON.parse(readFileSync2(hostFile(), "utf8"));
47786
+ return JSON.parse(readFileSync3(hostFile(), "utf8"));
47687
47787
  } catch {
47688
47788
  return null;
47689
47789
  }
@@ -47702,11 +47802,11 @@ async function runningHost() {
47702
47802
  }
47703
47803
  var SERVER = "Contents/Resources/server/ponzu";
47704
47804
  var pathFor = (os) => os === "win32" ? win32 : posix;
47705
- var appImageServer = () => join14(ponzuHome(), "app/ponzu");
47706
- var appImageRecord = () => join14(ponzuHome(), "app/appimage");
47805
+ var appImageServer = () => join15(ponzuHome(), "app/ponzu");
47806
+ var appImageRecord = () => join15(ponzuHome(), "app/appimage");
47707
47807
  function recordedAppImage() {
47708
47808
  try {
47709
- const path = readFileSync2(appImageRecord(), "utf8").trim();
47809
+ const path = readFileSync3(appImageRecord(), "utf8").trim();
47710
47810
  return path && existsSync9(path) ? path : null;
47711
47811
  } catch {
47712
47812
  return null;
@@ -47721,7 +47821,7 @@ function isFolder(app) {
47721
47821
  }
47722
47822
  function appServer(app, os = process.platform) {
47723
47823
  if (os === "darwin")
47724
- return join14(app, SERVER);
47824
+ return join15(app, SERVER);
47725
47825
  const path = pathFor(os);
47726
47826
  if (os === "linux" && !isFolder(app))
47727
47827
  return appImageServer();
@@ -47754,7 +47854,7 @@ function ownBundle(execPath = process.execPath, os = process.platform, env = pro
47754
47854
  }
47755
47855
  function installedApps(os = process.platform, env = process.env) {
47756
47856
  if (os === "darwin")
47757
- return ["/Applications/Ponzu.app", join14(homedir5(), "Applications/Ponzu.app")];
47857
+ return ["/Applications/Ponzu.app", join15(homedir6(), "Applications/Ponzu.app")];
47758
47858
  if (os === "win32") {
47759
47859
  return [
47760
47860
  env.LOCALAPPDATA && win32.join(env.LOCALAPPDATA, "Programs", "ponzu"),
@@ -47787,7 +47887,7 @@ async function adoptAppImage(execPath = process.execPath, os = process.platform,
47787
47887
  const from = statSync3(execPath);
47788
47888
  const to = statSync3(copy, { throwIfNoEntry: false });
47789
47889
  if (!to || to.size !== from.size || Math.trunc(to.mtimeMs) !== Math.trunc(from.mtimeMs)) {
47790
- await mkdir11(dirname6(copy), { recursive: true });
47890
+ await mkdir11(dirname7(copy), { recursive: true });
47791
47891
  const temp = `${copy}.${process.pid}`;
47792
47892
  await copyFile(execPath, temp);
47793
47893
  await chmod(temp, 493);
@@ -47802,7 +47902,7 @@ async function adoptAppImage(execPath = process.execPath, os = process.platform,
47802
47902
  async function launchApp(app, timeoutMs = 30000) {
47803
47903
  const deadline = Date.now() + timeoutMs;
47804
47904
  for (let tick = 0;Date.now() < deadline; tick++) {
47805
- if (tick % 40 === 0)
47905
+ if (tick % 40 === 0 && !updating())
47806
47906
  startApp(app);
47807
47907
  const url = await runningHost();
47808
47908
  if (url)
@@ -47851,7 +47951,7 @@ async function findHost(launch) {
47851
47951
  lastFailed = Date.now();
47852
47952
  return url;
47853
47953
  }
47854
- var shimPath = (os = process.platform) => join14(ponzuHome(), os === "win32" ? "bin/ponzu.cmd" : "bin/ponzu");
47954
+ var shimPath = (os = process.platform) => join15(ponzuHome(), os === "win32" ? "bin/ponzu.cmd" : "bin/ponzu");
47855
47955
  function shimScript(app, os = process.platform) {
47856
47956
  if (os === "win32") {
47857
47957
  const candidates = [
@@ -47896,7 +47996,7 @@ exit 1
47896
47996
  }
47897
47997
  async function writeShim(app) {
47898
47998
  const path = shimPath();
47899
- await mkdir11(dirname6(path), { recursive: true });
47999
+ await mkdir11(dirname7(path), { recursive: true });
47900
48000
  await writeFile9(path, shimScript(app));
47901
48001
  await chmod(path, 493);
47902
48002
  return path;
@@ -47904,28 +48004,28 @@ async function writeShim(app) {
47904
48004
 
47905
48005
  // apps/cli/src/install.ts
47906
48006
  import { spawnSync as spawnSync2 } from "child_process";
47907
- import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
48007
+ import { existsSync as existsSync11, readFileSync as readFileSync5 } from "fs";
47908
48008
  import { mkdir as mkdir13, writeFile as writeFile11 } from "fs/promises";
47909
- import { homedir as homedir7 } from "os";
47910
- import { delimiter as delimiter2, dirname as dirname8, join as join16 } from "path";
48009
+ import { homedir as homedir8 } from "os";
48010
+ import { delimiter as delimiter2, dirname as dirname9, join as join17 } from "path";
47911
48011
 
47912
48012
  // apps/cli/src/command.ts
47913
48013
  import { spawnSync } from "child_process";
47914
- import { accessSync, constants, existsSync as existsSync10, lstatSync, readFileSync as readFileSync3, realpathSync as realpathSync3 } from "fs";
48014
+ import { accessSync, constants, existsSync as existsSync10, lstatSync, readFileSync as readFileSync4, realpathSync as realpathSync3 } from "fs";
47915
48015
  import { mkdir as mkdir12, symlink, writeFile as writeFile10 } from "fs/promises";
47916
- import { homedir as homedir6 } from "os";
47917
- import { delimiter, dirname as dirname7, join as join15 } from "path";
47918
- function commandDirs(home = homedir6()) {
47919
- return [join15(home, ".local/bin"), join15(home, "bin"), "/usr/local/bin"];
48016
+ import { homedir as homedir7 } from "os";
48017
+ import { delimiter, dirname as dirname8, join as join16 } from "path";
48018
+ function commandDirs(home = homedir7()) {
48019
+ return [join16(home, ".local/bin"), join16(home, "bin"), "/usr/local/bin"];
47920
48020
  }
47921
- var stateFile = () => join15(ponzuHome(), "command.json");
48021
+ var stateFile = () => join16(ponzuHome(), "command.json");
47922
48022
  async function linkCommand(shim, opts) {
47923
48023
  const say = (s) => {
47924
48024
  if (!opts.auto)
47925
48025
  console.log(s);
47926
48026
  };
47927
48027
  if (process.platform === "win32")
47928
- return addToUserPath(dirname7(shim), opts.auto, say);
48028
+ return addToUserPath(dirname8(shim), opts.auto, say);
47929
48029
  const found = Bun.which("ponzu", { PATH: opts.path.join(delimiter) });
47930
48030
  const app = found && opensApp(found) ? found : null;
47931
48031
  if (found && !app) {
@@ -47936,16 +48036,16 @@ async function linkCommand(shim, opts) {
47936
48036
  const state = readState();
47937
48037
  if (opts.auto && state.linked)
47938
48038
  return;
47939
- const before = app ? opts.path.indexOf(dirname7(app)) : -1;
48039
+ const before = app ? opts.path.indexOf(dirname8(app)) : -1;
47940
48040
  const dir = commandDirs(opts.home).find((d) => {
47941
48041
  const at = opts.path.indexOf(d);
47942
48042
  return at >= 0 && (before < 0 || at < before) && canLink(d, opts.home);
47943
48043
  });
47944
48044
  if (!dir) {
47945
- say(process.platform === "darwin" ? `To run ponzu from a terminal, add ${dirname7(shim)} to your PATH, or choose Install Terminal Command in Ponzu's app menu.` : `To run ponzu from a terminal, add ${dirname7(shim)} to your PATH.`);
48045
+ say(process.platform === "darwin" ? `To run ponzu from a terminal, add ${dirname8(shim)} to your PATH, or choose Install Terminal Command in Ponzu's app menu.` : `To run ponzu from a terminal, add ${dirname8(shim)} to your PATH.`);
47946
48046
  return;
47947
48047
  }
47948
- const link = join15(dir, "ponzu");
48048
+ const link = join16(dir, "ponzu");
47949
48049
  try {
47950
48050
  await mkdir12(dir, { recursive: true });
47951
48051
  await symlink(shim, link);
@@ -47953,7 +48053,7 @@ async function linkCommand(shim, opts) {
47953
48053
  say(`Couldn't link ponzu into ${dir}.`);
47954
48054
  return;
47955
48055
  }
47956
- await mkdir12(dirname7(stateFile()), { recursive: true });
48056
+ await mkdir12(dirname8(stateFile()), { recursive: true });
47957
48057
  await writeFile10(stateFile(), `${JSON.stringify({ linked: link }, null, 2)}
47958
48058
  `);
47959
48059
  say(`Linked ${link}, so ponzu runs from a terminal.`);
@@ -47992,21 +48092,21 @@ async function addToUserPath(dir, auto, say) {
47992
48092
  return;
47993
48093
  if (userPath(dir, true) !== "added")
47994
48094
  return say(`Couldn't add ${dir} to your PATH. Add it yourself to run ponzu from a terminal.`);
47995
- await mkdir12(dirname7(stateFile()), { recursive: true });
48095
+ await mkdir12(dirname8(stateFile()), { recursive: true });
47996
48096
  await writeFile10(stateFile(), `${JSON.stringify({ linked: dir }, null, 2)}
47997
48097
  `);
47998
48098
  say(`Added ${dir} to your PATH. Open a new terminal and run ponzu.`);
47999
48099
  }
48000
48100
  function readState() {
48001
48101
  try {
48002
- return JSON.parse(readFileSync3(stateFile(), "utf8"));
48102
+ return JSON.parse(readFileSync4(stateFile(), "utf8"));
48003
48103
  } catch {
48004
48104
  return {};
48005
48105
  }
48006
48106
  }
48007
- function canLink(dir, home = homedir6()) {
48107
+ function canLink(dir, home = homedir7()) {
48008
48108
  try {
48009
- lstatSync(join15(dir, "ponzu"));
48109
+ lstatSync(join16(dir, "ponzu"));
48010
48110
  return false;
48011
48111
  } catch {}
48012
48112
  if (!existsSync10(dir))
@@ -48020,7 +48120,7 @@ function canLink(dir, home = homedir6()) {
48020
48120
  }
48021
48121
  function opensApp(file) {
48022
48122
  try {
48023
- return existsSync10(join15(dirname7(realpathSync3(file)), "resources/server/ponzu"));
48123
+ return existsSync10(join16(dirname8(realpathSync3(file)), "resources/server/ponzu"));
48024
48124
  } catch {
48025
48125
  return false;
48026
48126
  }
@@ -48040,7 +48140,7 @@ var AGENTS = [
48040
48140
  label: "Claude Code",
48041
48141
  detect: () => Boolean(which("claude")),
48042
48142
  current: () => {
48043
- const config = readJson(join16(homedir7(), ".claude.json"));
48143
+ const config = readJson(join17(homedir8(), ".claude.json"));
48044
48144
  return toEntry(config?.mcpServers?.ponzu);
48045
48145
  },
48046
48146
  register(entry) {
@@ -48090,7 +48190,7 @@ var AGENTS = [
48090
48190
  {
48091
48191
  id: "cursor",
48092
48192
  label: "Cursor",
48093
- detect: () => existsSync11(join16(homedir7(), ".cursor")),
48193
+ detect: () => existsSync11(join17(homedir8(), ".cursor")),
48094
48194
  current: () => {
48095
48195
  const config = readJson(cursorConfig());
48096
48196
  return toEntry(config?.mcpServers?.ponzu);
@@ -48108,7 +48208,7 @@ var AGENTS = [
48108
48208
  config = parsed;
48109
48209
  }
48110
48210
  config.mcpServers = { ...config.mcpServers, ponzu: entry };
48111
- await mkdir13(dirname8(path), { recursive: true });
48211
+ await mkdir13(dirname9(path), { recursive: true });
48112
48212
  await writeFile11(path, `${JSON.stringify(config, null, 2)}
48113
48213
  `);
48114
48214
  return true;
@@ -48177,7 +48277,7 @@ function agentStatus() {
48177
48277
  }
48178
48278
  function codexConfigured() {
48179
48279
  try {
48180
- return /^\[mcp_servers\.ponzu\]/m.test(readFileSync4(join16(process.env.CODEX_HOME ?? join16(homedir7(), ".codex"), "config.toml"), "utf8"));
48280
+ return /^\[mcp_servers\.ponzu\]/m.test(readFileSync5(join17(process.env.CODEX_HOME ?? join17(homedir8(), ".codex"), "config.toml"), "utf8"));
48181
48281
  } catch {
48182
48282
  return false;
48183
48283
  }
@@ -48221,22 +48321,22 @@ function shouldAutoRegister(current, setUpBefore) {
48221
48321
  function isLegacy(e) {
48222
48322
  return "url" in e && /^http:\/\/(127\.0\.0\.1|localhost):7777\/mcp\/?$/.test(e.url);
48223
48323
  }
48224
- var doneFile = () => join16(ponzuHome(), "agents.json");
48324
+ var doneFile = () => join17(ponzuHome(), "agents.json");
48225
48325
  function readDone() {
48226
48326
  const raw = readJson(doneFile());
48227
48327
  return new Set(raw?.configured ?? []);
48228
48328
  }
48229
48329
  async function writeDone(done) {
48230
- await mkdir13(dirname8(doneFile()), { recursive: true });
48330
+ await mkdir13(dirname9(doneFile()), { recursive: true });
48231
48331
  await writeFile11(doneFile(), `${JSON.stringify({ configured: [...done].sort() }, null, 2)}
48232
48332
  `);
48233
48333
  }
48234
48334
  function cursorConfig() {
48235
- return join16(homedir7(), ".cursor", "mcp.json");
48335
+ return join17(homedir8(), ".cursor", "mcp.json");
48236
48336
  }
48237
48337
  function readJson(path) {
48238
48338
  try {
48239
- return JSON.parse(readFileSync4(path, "utf8"));
48339
+ return JSON.parse(readFileSync5(path, "utf8"));
48240
48340
  } catch {
48241
48341
  return null;
48242
48342
  }
@@ -48245,13 +48345,13 @@ var searchPath;
48245
48345
  function agentPath() {
48246
48346
  if (searchPath !== undefined)
48247
48347
  return searchPath;
48248
- const home = homedir7();
48348
+ const home = homedir8();
48249
48349
  const dirs = [
48250
48350
  ...(process.env.PATH ?? "").split(delimiter2),
48251
- join16(home, ".local/bin"),
48252
- join16(home, ".claude/local"),
48253
- join16(home, ".bun/bin"),
48254
- join16(home, ".npm-global/bin"),
48351
+ join17(home, ".local/bin"),
48352
+ join17(home, ".claude/local"),
48353
+ join17(home, ".bun/bin"),
48354
+ join17(home, ".npm-global/bin"),
48255
48355
  "/opt/homebrew/bin",
48256
48356
  "/usr/local/bin",
48257
48357
  ...loginPath()
@@ -48311,13 +48411,13 @@ function run(cmd, quiet = false) {
48311
48411
  // apps/cli/src/source.ts
48312
48412
  import { createHash as createHash5 } from "crypto";
48313
48413
  import { existsSync as existsSync12 } from "fs";
48314
- import { dirname as dirname9, join as join17, sep as sep4 } from "path";
48414
+ import { dirname as dirname10, join as join18, sep as sep4 } from "path";
48315
48415
  import { fileURLToPath } from "url";
48316
48416
  function sourceCheckout(file = fileURLToPath(import.meta.url)) {
48317
48417
  if (file.startsWith("/$bunfs/") || file.split(sep4).includes("node_modules"))
48318
48418
  return null;
48319
- for (let dir = dirname9(file);dir !== dirname9(dir); dir = dirname9(dir))
48320
- if (existsSync12(join17(dir, ".git")))
48419
+ for (let dir = dirname10(file);dir !== dirname10(dir); dir = dirname10(dir))
48420
+ if (existsSync12(join18(dir, ".git")))
48321
48421
  return dir;
48322
48422
  return null;
48323
48423
  }
@@ -48355,13 +48455,13 @@ function firstFree(ports, hostname) {
48355
48455
 
48356
48456
  // apps/cli/src/telemetry.ts
48357
48457
  import { createHash as createHash6, randomUUID } from "crypto";
48358
- import { readFileSync as readFileSync5 } from "fs";
48458
+ import { readFileSync as readFileSync6 } from "fs";
48359
48459
  import { mkdir as mkdir14, rename as rename10, writeFile as writeFile12 } from "fs/promises";
48360
48460
  import { arch, platform as platform2 } from "os";
48361
- import { dirname as dirname11, join as join18 } from "path";
48461
+ import { dirname as dirname12, join as join19 } from "path";
48362
48462
 
48363
48463
  // node_modules/.bun/posthog-node@5.56.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
48364
- import { dirname as dirname10, posix as posix2, sep as sep5 } from "path";
48464
+ import { dirname as dirname11, posix as posix2, sep as sep5 } from "path";
48365
48465
  function createModulerModifier() {
48366
48466
  const getModuleFromFileName = createGetModuleFromFilename();
48367
48467
  return async (frames) => {
@@ -48370,7 +48470,7 @@ function createModulerModifier() {
48370
48470
  return frames;
48371
48471
  };
48372
48472
  }
48373
- function createGetModuleFromFilename(basePath = process.argv[1] ? dirname10(process.argv[1]) : process.cwd(), isWindows = sep5 === "\\") {
48473
+ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname11(process.argv[1]) : process.cwd(), isWindows = sep5 === "\\") {
48374
48474
  const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
48375
48475
  return (filename) => {
48376
48476
  if (!filename)
@@ -57650,17 +57750,17 @@ var CATEGORY = {
57650
57750
  praise: "Praise",
57651
57751
  other: "Other"
57652
57752
  };
57653
- var settingsFile = () => join18(ponzuHome(), "telemetry.json");
57753
+ var settingsFile = () => join19(ponzuHome(), "telemetry.json");
57654
57754
  function readSettings() {
57655
57755
  try {
57656
- return JSON.parse(readFileSync5(settingsFile(), "utf8"));
57756
+ return JSON.parse(readFileSync6(settingsFile(), "utf8"));
57657
57757
  } catch (err) {
57658
57758
  return err.code === "ENOENT" ? {} : null;
57659
57759
  }
57660
57760
  }
57661
57761
  async function writeSettings(settings) {
57662
57762
  const path = settingsFile();
57663
- await mkdir14(dirname11(path), { recursive: true });
57763
+ await mkdir14(dirname12(path), { recursive: true });
57664
57764
  const tmp = `${path}.${process.pid}.tmp`;
57665
57765
  await writeFile12(tmp, `${JSON.stringify(settings, null, 2)}
57666
57766
  `);
@@ -57836,14 +57936,14 @@ async function telemetryCommand(arg = "status") {
57836
57936
  // apps/cli/src/wake.ts
57837
57937
  import { dlopen, FFIType, ptr } from "bun:ffi";
57838
57938
  import { spawnSync as spawnSync3 } from "child_process";
57839
- import { fstatSync, readFileSync as readFileSync6 } from "fs";
57939
+ import { fstatSync, readFileSync as readFileSync7 } from "fs";
57840
57940
  import { mkdir as mkdir15, rm as rm6, writeFile as writeFile13 } from "fs/promises";
57841
- import { homedir as homedir8 } from "os";
57842
- import { join as join19 } from "path";
57941
+ import { homedir as homedir9 } from "os";
57942
+ import { join as join20 } from "path";
57843
57943
  var WAKE_LABEL = "design.ponzu.app.wake";
57844
57944
  var WAKE_PORT = 7778;
57845
- var agentsDir = () => process.env.PONZU_LAUNCH_AGENTS ?? join19(homedir8(), "Library/LaunchAgents");
57846
- var wakePlistPath = () => join19(agentsDir(), `${WAKE_LABEL}.plist`);
57945
+ var agentsDir = () => process.env.PONZU_LAUNCH_AGENTS ?? join20(homedir9(), "Library/LaunchAgents");
57946
+ var wakePlistPath = () => join20(agentsDir(), `${WAKE_LABEL}.plist`);
57847
57947
  var xml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
57848
57948
  function wakePlist(program, port = WAKE_PORT) {
57849
57949
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -57885,7 +57985,7 @@ async function installWake(program, run = launchctl) {
57885
57985
  const plist = wakePlist(program);
57886
57986
  let current = null;
57887
57987
  try {
57888
- current = readFileSync6(path, "utf8");
57988
+ current = readFileSync7(path, "utf8");
57889
57989
  } catch {}
57890
57990
  if (current === plist && run(["print", `${domain()}/${WAKE_LABEL}`]))
57891
57991
  return false;
@@ -57899,7 +57999,7 @@ async function uninstallWake(run = launchctl) {
57899
57999
  run(["bootout", `${domain()}/${WAKE_LABEL}`]);
57900
58000
  await rm6(wakePlistPath(), { force: true });
57901
58001
  }
57902
- var systemdDir = () => process.env.PONZU_SYSTEMD_USER ?? join19(process.env.XDG_CONFIG_HOME || join19(homedir8(), ".config"), "systemd/user");
58002
+ var systemdDir = () => process.env.PONZU_SYSTEMD_USER ?? join20(process.env.XDG_CONFIG_HOME || join20(homedir9(), ".config"), "systemd/user");
57903
58003
  var WAKE_SOCKET = "ponzu-wake.socket";
57904
58004
  var WAKE_SERVICE = "ponzu-wake@.service";
57905
58005
  var systemdWord = (s) => `"${s.replace(/[\\"]/g, "\\$&").replace(/%/g, "%%").replace(/\$/g, "$$$$")}"`;
@@ -57933,7 +58033,7 @@ async function installSystemdWake(program, run = systemctl) {
57933
58033
  const units = wakeUnits(program);
57934
58034
  const current = Object.entries(units).every(([name, text]) => {
57935
58035
  try {
57936
- return readFileSync6(join19(systemdDir(), name), "utf8") === text;
58036
+ return readFileSync7(join20(systemdDir(), name), "utf8") === text;
57937
58037
  } catch {
57938
58038
  return false;
57939
58039
  }
@@ -57942,7 +58042,7 @@ async function installSystemdWake(program, run = systemctl) {
57942
58042
  return false;
57943
58043
  await mkdir15(systemdDir(), { recursive: true });
57944
58044
  for (const [name, text] of Object.entries(units))
57945
- await writeFile13(join19(systemdDir(), name), text);
58045
+ await writeFile13(join20(systemdDir(), name), text);
57946
58046
  run(["daemon-reload"]);
57947
58047
  run(["enable", WAKE_SOCKET]);
57948
58048
  run(["restart", WAKE_SOCKET]);
@@ -57951,7 +58051,7 @@ async function installSystemdWake(program, run = systemctl) {
57951
58051
  async function uninstallSystemdWake(run = systemctl) {
57952
58052
  run(["disable", "--now", WAKE_SOCKET]);
57953
58053
  for (const name of [WAKE_SOCKET, WAKE_SERVICE])
57954
- await rm6(join19(systemdDir(), name), { force: true });
58054
+ await rm6(join20(systemdDir(), name), { force: true });
57955
58055
  run(["daemon-reload"]);
57956
58056
  }
57957
58057
  function peerKind(ip) {
@@ -58116,7 +58216,7 @@ function lsofPeer() {
58116
58216
 
58117
58217
  // apps/cli/src/index.ts
58118
58218
  var VERSION2 = package_default.version;
58119
- var here = dirname12(fileURLToPath2(import.meta.url));
58219
+ var here = dirname13(fileURLToPath2(import.meta.url));
58120
58220
  var HELP = `Ponzu ${VERSION2}: the open source canvas your agent draws on in HTML and CSS.
58121
58221
 
58122
58222
  Usage
@@ -58241,7 +58341,7 @@ async function openWorkspace(files, log) {
58241
58341
  const paths = files.length ? files.map((f) => resolve5(f)) : await findDesigns(process.cwd());
58242
58342
  const named = [];
58243
58343
  for (const p of paths) {
58244
- if (!existsSync13(join20(p, "design.json")) && existsSync13(p) === false && files.length) {
58344
+ if (!existsSync13(join21(p, "design.json")) && existsSync13(p) === false && files.length) {
58245
58345
  log(`Creating ${p}`);
58246
58346
  }
58247
58347
  const { key } = await ws.open(p);
@@ -58253,7 +58353,8 @@ async function openWorkspace(files, log) {
58253
58353
  telemetry?.capture("host started", {
58254
58354
  launched_by: process.env.PONZU_LAUNCHED_BY ?? "person",
58255
58355
  designs: ws.designs().length,
58256
- renderer: Boolean(renderer)
58356
+ renderer: Boolean(renderer),
58357
+ ...process.env.PONZU_UPDATE ? { update: process.env.PONZU_UPDATE } : {}
58257
58358
  });
58258
58359
  return { ws, telemetry, named };
58259
58360
  }
@@ -58267,14 +58368,14 @@ async function runServe(files, o) {
58267
58368
  root: process.cwd(),
58268
58369
  tailnet: o.tailnet,
58269
58370
  tailnetPort: 7777,
58270
- dbFile: join20(ponzuHome(), "ponzu.db"),
58271
- stateFile: join20(ponzuHome(), "catalog.json"),
58371
+ dbFile: join21(ponzuHome(), "ponzu.db"),
58372
+ stateFile: join21(ponzuHome(), "catalog.json"),
58272
58373
  spotlight: process.env.PONZU_SPOTLIGHT === "0" ? false : undefined,
58273
58374
  agents: async () => agents(),
58274
58375
  suggest: {
58275
58376
  bin: (agent) => agentCli(agent),
58276
58377
  env: agentEnv(),
58277
- file: join20(ponzuHome(), "suggestions.json")
58378
+ file: join21(ponzuHome(), "suggestions.json")
58278
58379
  },
58279
58380
  opened: named,
58280
58381
  version: VERSION2,
@@ -58381,7 +58482,7 @@ function absolutePaths(args, cwd) {
58381
58482
  }
58382
58483
  async function runInit(target = "design/untitled.design") {
58383
58484
  const path = resolve5(target.endsWith(".design") ? target : `${target}.design`);
58384
- if (existsSync13(join20(path, "design.json"))) {
58485
+ if (existsSync13(join21(path, "design.json"))) {
58385
58486
  console.error(`${path} already exists.`);
58386
58487
  process.exit(1);
58387
58488
  }
@@ -58403,11 +58504,11 @@ Next: ponzu serve ${target}`);
58403
58504
  }
58404
58505
  function findStudio() {
58405
58506
  const candidates = [
58406
- join20(here, "../studio"),
58407
- join20(here, "../../studio/dist"),
58408
- join20(here, "../../../apps/studio/dist")
58507
+ join21(here, "../studio"),
58508
+ join21(here, "../../studio/dist"),
58509
+ join21(here, "../../../apps/studio/dist")
58409
58510
  ];
58410
- return candidates.find((c) => existsSync13(join20(c, "index.html")));
58511
+ return candidates.find((c) => existsSync13(join21(c, "index.html")));
58411
58512
  }
58412
58513
  function lanAddresses() {
58413
58514
  const out = [];