@poncho-ai/cli 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/cli@0.26.0 build /home/runner/work/poncho-ai/poncho-ai/packages/cli
2
+ > @poncho-ai/cli@0.27.0 build /home/runner/work/poncho-ai/poncho-ai/packages/cli
3
3
  > tsup src/index.ts src/cli.ts --format esm --dts
4
4
 
5
5
  CLI Building entry: src/cli.ts, src/index.ts
@@ -9,10 +9,10 @@
9
9
  ESM Build start
10
10
  ESM dist/cli.js 94.00 B
11
11
  ESM dist/index.js 857.00 B
12
- ESM dist/run-interactive-ink-4YCKM2JH.js 56.74 KB
13
- ESM dist/chunk-P3D4E4FZ.js 421.62 KB
12
+ ESM dist/run-interactive-ink-OUX42GU4.js 56.74 KB
13
+ ESM dist/chunk-LPT2RFZ5.js 422.53 KB
14
14
  ESM ⚡️ Build success in 63ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 3809ms
16
+ DTS ⚡️ Build success in 4319ms
17
17
  DTS dist/cli.d.ts 20.00 B
18
18
  DTS dist/index.d.ts 3.70 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @poncho-ai/cli
2
2
 
3
+ ## 0.27.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`aee4f17`](https://github.com/cesr/poncho-ai/commit/aee4f17237d33b2cc134ed9934b709d967ca3f10) Thanks [@cesr](https://github.com/cesr)! - Add `edit_file` built-in tool with str_replace semantics for targeted file edits. The tool takes `path`, `old_str`, and `new_str` parameters, enforces uniqueness of the match, and is write-gated like `write_file` (disabled in production by default). Also improves browser SSE frame streaming with backpressure handling and auto-stops screencast when all listeners disconnect.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`aee4f17`](https://github.com/cesr/poncho-ai/commit/aee4f17237d33b2cc134ed9934b709d967ca3f10)]:
12
+ - @poncho-ai/harness@0.24.0
13
+
3
14
  ## 0.26.0
4
15
 
5
16
  ### Minor Changes
@@ -5126,6 +5126,7 @@ self.addEventListener("fetch", (event) => {
5126
5126
  var renderWebUiHtml = (options) => {
5127
5127
  const agentInitial = (options?.agentName ?? "A").charAt(0).toUpperCase();
5128
5128
  const agentName = options?.agentName ?? "Agent";
5129
+ const pageTitle = options?.isDev ? `[dev] ${agentName}` : agentName;
5129
5130
  return `<!doctype html>
5130
5131
  <html lang="en">
5131
5132
  <head>
@@ -5139,7 +5140,7 @@ var renderWebUiHtml = (options) => {
5139
5140
  <link rel="manifest" href="/manifest.json">
5140
5141
  <link rel="icon" href="/icon.svg" type="image/svg+xml">
5141
5142
  <link rel="apple-touch-icon" href="/icon-192.png">
5142
- <title>${agentName}</title>
5143
+ <title>${pageTitle}</title>
5143
5144
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inconsolata:400,700">
5144
5145
  <style>
5145
5146
  ${WEB_UI_STYLES}
@@ -6774,12 +6775,14 @@ export default {
6774
6775
  list_directory: true,
6775
6776
  read_file: true,
6776
6777
  write_file: true, // gated by environment for writes
6778
+ edit_file: true, // gated by environment for writes
6777
6779
  delete_file: 'approval', // requires human approval
6778
6780
  delete_directory: 'approval', // requires human approval
6779
6781
  send_email: 'approval', // requires human approval
6780
6782
  byEnvironment: {
6781
6783
  production: {
6782
6784
  write_file: false,
6785
+ edit_file: false,
6783
6786
  delete_file: false,
6784
6787
  delete_directory: false,
6785
6788
  },
@@ -8847,7 +8850,7 @@ data: ${JSON.stringify(statusPayload)}
8847
8850
  const requestUrl = new URL(request.url ?? "/", `http://${request.headers.host ?? "localhost"}`);
8848
8851
  if (webUiEnabled) {
8849
8852
  if (request.method === "GET" && (pathname === "/" || pathname.startsWith("/c/"))) {
8850
- writeHtml(response, 200, renderWebUiHtml({ agentName }));
8853
+ writeHtml(response, 200, renderWebUiHtml({ agentName, isDev: !isProduction }));
8851
8854
  return;
8852
8855
  }
8853
8856
  if (pathname === "/manifest.json" && request.method === "GET") {
@@ -9016,6 +9019,9 @@ data: ${JSON.stringify(statusPayload)}
9016
9019
  });
9017
9020
  response.flushHeaders();
9018
9021
  let frameCount = 0;
9022
+ let droppedFrames = 0;
9023
+ let draining = false;
9024
+ let pendingFrame = null;
9019
9025
  const sendSse = (event, data) => {
9020
9026
  if (response.destroyed) return;
9021
9027
  response.write(`event: ${event}
@@ -9023,6 +9029,29 @@ data: ${JSON.stringify(data)}
9023
9029
 
9024
9030
  `);
9025
9031
  };
9032
+ const sendFrame = (frame) => {
9033
+ if (response.destroyed) return;
9034
+ if (draining) {
9035
+ pendingFrame = frame;
9036
+ droppedFrames++;
9037
+ return;
9038
+ }
9039
+ const ok = response.write(`event: browser:frame
9040
+ data: ${JSON.stringify(frame)}
9041
+
9042
+ `);
9043
+ if (!ok) {
9044
+ draining = true;
9045
+ response.once("drain", () => {
9046
+ draining = false;
9047
+ if (pendingFrame && !response.destroyed) {
9048
+ const f = pendingFrame;
9049
+ pendingFrame = null;
9050
+ sendFrame(f);
9051
+ }
9052
+ });
9053
+ }
9054
+ };
9026
9055
  sendSse("browser:status", {
9027
9056
  active: browserSession.isActiveFor(cid),
9028
9057
  url: browserSession.getUrl(cid),
@@ -9031,9 +9060,9 @@ data: ${JSON.stringify(data)}
9031
9060
  const removeFrame = browserSession.onFrame(cid, (frame) => {
9032
9061
  frameCount++;
9033
9062
  if (frameCount <= 3 || frameCount % 50 === 0) {
9034
- console.log(`[poncho][browser-sse] Frame ${frameCount}: ${frame.width}x${frame.height}, data bytes: ${frame.data?.length ?? 0}`);
9063
+ console.log(`[poncho][browser-sse] Frame ${frameCount}: ${frame.width}x${frame.height}, data bytes: ${frame.data?.length ?? 0}${droppedFrames > 0 ? `, dropped: ${droppedFrames}` : ""}`);
9035
9064
  }
9036
- sendSse("browser:frame", frame);
9065
+ sendFrame(frame);
9037
9066
  });
9038
9067
  const removeStatus = browserSession.onStatus(cid, (status) => {
9039
9068
  sendSse("browser:status", status);
@@ -9041,7 +9070,7 @@ data: ${JSON.stringify(data)}
9041
9070
  if (browserSession.isActiveFor(cid)) {
9042
9071
  browserSession.screenshot(cid).then((data) => {
9043
9072
  if (!response.destroyed) {
9044
- sendSse("browser:frame", { data, width: 1280, height: 720, timestamp: Date.now() });
9073
+ sendFrame({ data, width: 1280, height: 720, timestamp: Date.now() });
9045
9074
  }
9046
9075
  return browserSession.startScreencast(cid);
9047
9076
  }).catch((err) => {
@@ -9051,6 +9080,7 @@ data: ${JSON.stringify(data)}
9051
9080
  request.on("close", () => {
9052
9081
  removeFrame();
9053
9082
  removeStatus();
9083
+ pendingFrame = null;
9054
9084
  });
9055
9085
  return;
9056
9086
  }
@@ -10583,7 +10613,7 @@ var runInteractive = async (workingDir, params) => {
10583
10613
  await harness.initialize();
10584
10614
  const identity = await ensureAgentIdentity2(workingDir);
10585
10615
  try {
10586
- const { runInteractiveInk } = await import("./run-interactive-ink-4YCKM2JH.js");
10616
+ const { runInteractiveInk } = await import("./run-interactive-ink-OUX42GU4.js");
10587
10617
  await runInteractiveInk({
10588
10618
  harness,
10589
10619
  params,
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-P3D4E4FZ.js";
4
+ } from "./chunk-LPT2RFZ5.js";
5
5
 
6
6
  // src/cli.ts
7
7
  void main();
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  runTests,
24
24
  startDevServer,
25
25
  updateAgentGuidance
26
- } from "./chunk-P3D4E4FZ.js";
26
+ } from "./chunk-LPT2RFZ5.js";
27
27
  export {
28
28
  addSkill,
29
29
  buildCli,
@@ -2,7 +2,7 @@ import {
2
2
  consumeFirstRunIntro,
3
3
  inferConversationTitle,
4
4
  resolveHarnessEnvironment
5
- } from "./chunk-P3D4E4FZ.js";
5
+ } from "./chunk-LPT2RFZ5.js";
6
6
 
7
7
  // src/run-interactive-ink.ts
8
8
  import * as readline from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/cli",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "CLI for building and deploying AI agents",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  "react": "^19.2.4",
28
28
  "react-devtools-core": "^6.1.5",
29
29
  "yaml": "^2.8.1",
30
- "@poncho-ai/harness": "0.23.0",
30
+ "@poncho-ai/harness": "0.24.0",
31
31
  "@poncho-ai/messaging": "0.7.0",
32
32
  "@poncho-ai/sdk": "1.5.0"
33
33
  },
package/src/index.ts CHANGED
@@ -550,12 +550,14 @@ export default {
550
550
  list_directory: true,
551
551
  read_file: true,
552
552
  write_file: true, // gated by environment for writes
553
+ edit_file: true, // gated by environment for writes
553
554
  delete_file: 'approval', // requires human approval
554
555
  delete_directory: 'approval', // requires human approval
555
556
  send_email: 'approval', // requires human approval
556
557
  byEnvironment: {
557
558
  production: {
558
559
  write_file: false,
560
+ edit_file: false,
559
561
  delete_file: false,
560
562
  delete_directory: false,
561
563
  },
@@ -2892,7 +2894,7 @@ export const createRequestHandler = async (options?: {
2892
2894
 
2893
2895
  if (webUiEnabled) {
2894
2896
  if (request.method === "GET" && (pathname === "/" || pathname.startsWith("/c/"))) {
2895
- writeHtml(response, 200, renderWebUiHtml({ agentName }));
2897
+ writeHtml(response, 200, renderWebUiHtml({ agentName, isDev: !isProduction }));
2896
2898
  return;
2897
2899
  }
2898
2900
 
@@ -3112,11 +3114,36 @@ export const createRequestHandler = async (options?: {
3112
3114
  response.flushHeaders();
3113
3115
 
3114
3116
  let frameCount = 0;
3117
+ let droppedFrames = 0;
3118
+ let draining = false;
3119
+ let pendingFrame: { data: string; width: number; height: number; timestamp: number } | null = null;
3120
+
3115
3121
  const sendSse = (event: string, data: unknown) => {
3116
3122
  if (response.destroyed) return;
3117
3123
  response.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
3118
3124
  };
3119
3125
 
3126
+ const sendFrame = (frame: { data: string; width: number; height: number; timestamp: number }) => {
3127
+ if (response.destroyed) return;
3128
+ if (draining) {
3129
+ pendingFrame = frame;
3130
+ droppedFrames++;
3131
+ return;
3132
+ }
3133
+ const ok = response.write(`event: browser:frame\ndata: ${JSON.stringify(frame)}\n\n`);
3134
+ if (!ok) {
3135
+ draining = true;
3136
+ response.once("drain", () => {
3137
+ draining = false;
3138
+ if (pendingFrame && !response.destroyed) {
3139
+ const f = pendingFrame;
3140
+ pendingFrame = null;
3141
+ sendFrame(f);
3142
+ }
3143
+ });
3144
+ }
3145
+ };
3146
+
3120
3147
  sendSse("browser:status", {
3121
3148
  active: browserSession.isActiveFor(cid),
3122
3149
  url: browserSession.getUrl(cid),
@@ -3126,20 +3153,18 @@ export const createRequestHandler = async (options?: {
3126
3153
  const removeFrame = browserSession.onFrame(cid, (frame) => {
3127
3154
  frameCount++;
3128
3155
  if (frameCount <= 3 || frameCount % 50 === 0) {
3129
- console.log(`[poncho][browser-sse] Frame ${frameCount}: ${frame.width}x${frame.height}, data bytes: ${frame.data?.length ?? 0}`);
3156
+ console.log(`[poncho][browser-sse] Frame ${frameCount}: ${frame.width}x${frame.height}, data bytes: ${frame.data?.length ?? 0}${droppedFrames > 0 ? `, dropped: ${droppedFrames}` : ""}`);
3130
3157
  }
3131
- sendSse("browser:frame", frame);
3158
+ sendFrame(frame);
3132
3159
  });
3133
3160
  const removeStatus = browserSession.onStatus(cid, (status) => {
3134
3161
  sendSse("browser:status", status);
3135
3162
  });
3136
3163
 
3137
3164
  if (browserSession.isActiveFor(cid)) {
3138
- // Send a screenshot as the first frame for immediate visual feedback,
3139
- // then start the live screencast for subsequent frames.
3140
3165
  browserSession.screenshot(cid).then((data) => {
3141
3166
  if (!response.destroyed) {
3142
- sendSse("browser:frame", { data, width: 1280, height: 720, timestamp: Date.now() });
3167
+ sendFrame({ data, width: 1280, height: 720, timestamp: Date.now() });
3143
3168
  }
3144
3169
  return browserSession.startScreencast(cid);
3145
3170
  }).catch((err: unknown) => {
@@ -3150,6 +3175,7 @@ export const createRequestHandler = async (options?: {
3150
3175
  request.on("close", () => {
3151
3176
  removeFrame();
3152
3177
  removeStatus();
3178
+ pendingFrame = null;
3153
3179
  });
3154
3180
  return;
3155
3181
  }
package/src/web-ui.ts CHANGED
@@ -92,9 +92,10 @@ self.addEventListener("fetch", (event) => {
92
92
  });
93
93
  `;
94
94
 
95
- export const renderWebUiHtml = (options?: { agentName?: string }): string => {
95
+ export const renderWebUiHtml = (options?: { agentName?: string; isDev?: boolean }): string => {
96
96
  const agentInitial = (options?.agentName ?? "A").charAt(0).toUpperCase();
97
97
  const agentName = options?.agentName ?? "Agent";
98
+ const pageTitle = options?.isDev ? `[dev] ${agentName}` : agentName;
98
99
  return `<!doctype html>
99
100
  <html lang="en">
100
101
  <head>
@@ -108,7 +109,7 @@ export const renderWebUiHtml = (options?: { agentName?: string }): string => {
108
109
  <link rel="manifest" href="/manifest.json">
109
110
  <link rel="icon" href="/icon.svg" type="image/svg+xml">
110
111
  <link rel="apple-touch-icon" href="/icon-192.png">
111
- <title>${agentName}</title>
112
+ <title>${pageTitle}</title>
112
113
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inconsolata:400,700">
113
114
  <style>
114
115
  ${WEB_UI_STYLES}