sparkecoder 0.1.69 → 0.1.70

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 (115) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +11 -4
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +75 -62
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +2 -2
  7. package/dist/{index-DqaHLgSC.d.ts → index-Dm6wGcYv.d.ts} +19 -19
  8. package/dist/index.d.ts +5 -5
  9. package/dist/index.js +75 -62
  10. package/dist/index.js.map +1 -1
  11. package/dist/{schema-Bq4tID-f.d.ts → schema-XcP0dedO.d.ts} +3 -3
  12. package/dist/{search-BRnGaIl-.d.ts → search-CCffrVJE.d.ts} +7 -7
  13. package/dist/server/index.js +75 -62
  14. package/dist/server/index.js.map +1 -1
  15. package/dist/skills/default/qa.md +376 -106
  16. package/dist/tools/index.d.ts +2 -2
  17. package/package.json +1 -1
  18. package/src/skills/default/qa.md +376 -106
  19. package/web/.next/BUILD_ID +1 -1
  20. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  21. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  22. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  23. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  24. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  25. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  26. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  27. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  41. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  42. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
  49. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  50. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
  60. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
  66. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
  67. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  68. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
  69. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
  70. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  74. package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
  75. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
  76. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
  78. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
  79. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/embed/[id]/page.js.nft.json +1 -1
  82. package/web/.next/standalone/web/.next/server/app/embed/[id]/page_client-reference-manifest.js +1 -1
  83. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  84. package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
  85. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  86. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  87. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  88. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  90. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  91. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c71f29f9._.js → [root-of-the-server]__a1877334._.js} +4 -4
  92. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  93. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  94. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  95. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  96. package/web/.next/standalone/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
  97. package/web/.next/standalone/web/.next/static/chunks/{b6ec74cad9ffd3ee.js → f95d41079838994a.js} +3 -3
  98. package/web/.next/standalone/web/.next/static/static/chunks/41a5c049931b2c77.css +1 -0
  99. package/web/.next/standalone/web/.next/static/static/chunks/{b6ec74cad9ffd3ee.js → f95d41079838994a.js} +3 -3
  100. package/web/.next/standalone/web/src/components/ai-elements/read-file-tool.tsx +19 -2
  101. package/web/.next/standalone/web/src/components/chat-interface.tsx +49 -4
  102. package/web/.next/static/chunks/41a5c049931b2c77.css +1 -0
  103. package/web/.next/static/chunks/{b6ec74cad9ffd3ee.js → f95d41079838994a.js} +3 -3
  104. package/web/.next/standalone/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
  105. package/web/.next/standalone/web/.next/static/static/chunks/fd39dd62879495e1.css +0 -1
  106. package/web/.next/static/chunks/fd39dd62879495e1.css +0 -1
  107. /package/web/.next/standalone/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  108. /package/web/.next/standalone/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  109. /package/web/.next/standalone/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
  110. /package/web/.next/standalone/web/.next/static/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  111. /package/web/.next/standalone/web/.next/static/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  112. /package/web/.next/standalone/web/.next/static/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
  113. /package/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_buildManifest.js +0 -0
  114. /package/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_clientMiddlewareManifest.json +0 -0
  115. /package/web/.next/static/{XB638PEDChQhwk6wSMrSh → PpaOWDfndYJrA-tJYr7gU}/_ssgManifest.js +0 -0
@@ -85,7 +85,7 @@ declare const sessions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
85
85
  tableName: "sessions";
86
86
  dataType: "string";
87
87
  columnType: "SQLiteText";
88
- data: "active" | "waiting" | "completed" | "error";
88
+ data: "error" | "completed" | "active" | "waiting";
89
89
  driverParam: string;
90
90
  notNull: true;
91
91
  hasDefault: true;
@@ -391,7 +391,7 @@ declare const toolExecutions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
391
391
  tableName: "tool_executions";
392
392
  dataType: "string";
393
393
  columnType: "SQLiteText";
394
- data: "completed" | "error" | "pending" | "approved" | "rejected";
394
+ data: "error" | "completed" | "pending" | "approved" | "rejected";
395
395
  driverParam: string;
396
396
  notNull: true;
397
397
  hasDefault: true;
@@ -967,7 +967,7 @@ declare const activeStreams: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
967
967
  tableName: "active_streams";
968
968
  dataType: "string";
969
969
  columnType: "SQLiteText";
970
- data: "active" | "error" | "finished";
970
+ data: "error" | "active" | "finished";
971
971
  driverParam: string;
972
972
  notNull: true;
973
973
  hasDefault: true;
@@ -15,12 +15,12 @@ interface BashToolOptions {
15
15
  }
16
16
  declare function createBashTool(options: BashToolOptions): ai.Tool<{
17
17
  background: boolean;
18
- id?: string | undefined;
19
18
  input?: string | undefined;
19
+ id?: string | undefined;
20
20
  command?: string | undefined;
21
21
  kill?: boolean | undefined;
22
22
  tail?: number | undefined;
23
- key?: "Enter" | "Escape" | "Up" | "Down" | "Left" | "Right" | "Tab" | "C-c" | "C-d" | "y" | "n" | undefined;
23
+ key?: "y" | "Enter" | "Escape" | "Up" | "Down" | "Left" | "Right" | "Tab" | "C-c" | "C-d" | "n" | undefined;
24
24
  }, {
25
25
  success: boolean;
26
26
  id: string;
@@ -41,7 +41,7 @@ declare function createBashTool(options: BashToolOptions): ai.Tool<{
41
41
  success: boolean;
42
42
  id: string;
43
43
  output: string;
44
- status: "running" | "stopped" | "unknown";
44
+ status: "unknown" | "running" | "stopped";
45
45
  message: string;
46
46
  error?: undefined;
47
47
  exitCode?: undefined;
@@ -49,7 +49,7 @@ declare function createBashTool(options: BashToolOptions): ai.Tool<{
49
49
  success: boolean;
50
50
  id: string;
51
51
  output: string;
52
- status: "running" | "stopped" | "unknown";
52
+ status: "unknown" | "running" | "stopped";
53
53
  message?: undefined;
54
54
  error?: undefined;
55
55
  exitCode?: undefined;
@@ -66,7 +66,7 @@ declare function createBashTool(options: BashToolOptions): ai.Tool<{
66
66
  id: string;
67
67
  output: string;
68
68
  exitCode: number;
69
- status: "completed" | "error" | "running" | "stopped";
69
+ status: "error" | "completed" | "running" | "stopped";
70
70
  message?: undefined;
71
71
  error?: undefined;
72
72
  } | {
@@ -116,8 +116,8 @@ interface WriteFileToolOptions {
116
116
  onProgress?: (progress: WriteFileProgress) => void;
117
117
  }
118
118
  declare function createWriteFileTool(options: WriteFileToolOptions): ai.Tool<{
119
- mode: "full" | "str_replace";
120
119
  path: string;
120
+ mode: "full" | "str_replace";
121
121
  content?: string | undefined;
122
122
  old_string?: string | undefined;
123
123
  new_string?: string | undefined;
@@ -218,8 +218,8 @@ interface SearchToolOptions {
218
218
  * Progress is streamed back to the UI so users can see exploration happening.
219
219
  */
220
220
  declare function createSearchTool(options: SearchToolOptions): ai.Tool<{
221
- query: string;
222
221
  context: string;
222
+ query: string;
223
223
  }, {
224
224
  success: boolean;
225
225
  error: string;
@@ -1858,10 +1858,6 @@ var init_stream_proxy = __esm({
1858
1858
  console.log(`[BROWSER-WS] Connected to ${url} (after ${this.reconnectAttempts} retries)`);
1859
1859
  this.reconnectAttempts = 0;
1860
1860
  this._connected = true;
1861
- this.emit("status", {
1862
- connected: true,
1863
- screencasting: true
1864
- });
1865
1861
  });
1866
1862
  this.ws.on("message", (raw) => {
1867
1863
  try {
@@ -1947,6 +1943,17 @@ var init_stream_proxy = __esm({
1947
1943
  this.ws.send(JSON.stringify(event));
1948
1944
  }
1949
1945
  }
1946
+ /**
1947
+ * Ask the StreamServer to send its current status (triggers sendStatus and
1948
+ * re-evaluates screencasting). Useful when listeners are replaced on a new
1949
+ * stream and we want a fresh status event.
1950
+ */
1951
+ requestStatus() {
1952
+ if (this.ws?.readyState === WebSocket.OPEN) {
1953
+ console.log(`[BROWSER-WS] Requesting fresh status from StreamServer`);
1954
+ this.ws.send(JSON.stringify({ type: "status" }));
1955
+ }
1956
+ }
1950
1957
  destroy() {
1951
1958
  console.log(`[BROWSER-WS] Destroying proxy for port ${this.port} (emitted ${this.frameCount} frames, throttled ${this.throttledCount})`);
1952
1959
  this.destroyed = true;
@@ -8492,37 +8499,40 @@ ${prompt}` });
8492
8499
  recorder.start();
8493
8500
  sessionRecorders.set(sessionId, recorder);
8494
8501
  }
8495
- if (proxy.listenerCount("frame") === 0) {
8496
- console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
8497
- proxy.on("frame", (frame) => {
8498
- sseBrowserFrameCount++;
8499
- if (sseBrowserFrameCount === 1) {
8500
- console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
8501
- } else if (sseBrowserFrameCount % 50 === 0) {
8502
- console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
8503
- }
8504
- const rec = sessionRecorders.get(sessionId);
8505
- rec?.addFrame(frame);
8506
- writeSSE(JSON.stringify({
8507
- type: "browser-frame",
8508
- data: frame.data,
8509
- metadata: frame.metadata
8510
- })).catch((err) => {
8511
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8512
- });
8502
+ const oldFrameListeners = proxy.listenerCount("frame");
8503
+ if (oldFrameListeners > 0) {
8504
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
8505
+ proxy.removeAllListeners("frame");
8506
+ proxy.removeAllListeners("status");
8507
+ }
8508
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
8509
+ proxy.on("frame", (frame) => {
8510
+ sseBrowserFrameCount++;
8511
+ if (sseBrowserFrameCount === 1) {
8512
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
8513
+ } else if (sseBrowserFrameCount % 50 === 0) {
8514
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
8515
+ }
8516
+ const rec = sessionRecorders.get(sessionId);
8517
+ rec?.addFrame(frame);
8518
+ writeSSE(JSON.stringify({
8519
+ type: "browser-frame",
8520
+ data: frame.data,
8521
+ metadata: frame.metadata
8522
+ })).catch((err) => {
8523
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
8513
8524
  });
8514
- proxy.on("status", (s) => {
8515
- console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
8516
- writeSSE(JSON.stringify({
8517
- type: "browser-status",
8518
- ...s
8519
- })).catch((err) => {
8520
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8521
- });
8525
+ });
8526
+ proxy.on("status", (s) => {
8527
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
8528
+ writeSSE(JSON.stringify({
8529
+ type: "browser-status",
8530
+ ...s
8531
+ })).catch((err) => {
8532
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
8522
8533
  });
8523
- } else {
8524
- console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
8525
- }
8534
+ });
8535
+ proxy.requestStatus();
8526
8536
  }
8527
8537
  },
8528
8538
  onStepFinish: async () => {
@@ -9017,37 +9027,40 @@ agents.post(
9017
9027
  recorder.start();
9018
9028
  sessionRecorders.set(session.id, recorder);
9019
9029
  }
9020
- if (proxy.listenerCount("frame") === 0) {
9021
- console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9022
- proxy.on("frame", (frame) => {
9023
- sseBrowserFrameCount++;
9024
- if (sseBrowserFrameCount === 1) {
9025
- console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9026
- } else if (sseBrowserFrameCount % 50 === 0) {
9027
- console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9028
- }
9029
- const rec = sessionRecorders.get(session.id);
9030
- rec?.addFrame(frame);
9031
- writeSSE(JSON.stringify({
9032
- type: "browser-frame",
9033
- data: frame.data,
9034
- metadata: frame.metadata
9035
- })).catch((err) => {
9036
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
9037
- });
9030
+ const oldFrameListeners = proxy.listenerCount("frame");
9031
+ if (oldFrameListeners > 0) {
9032
+ console.log(`[BROWSER-STREAM:${streamId}] Replacing ${oldFrameListeners} stale frame listener(s) from previous stream`);
9033
+ proxy.removeAllListeners("frame");
9034
+ proxy.removeAllListeners("status");
9035
+ }
9036
+ console.log(`[BROWSER-STREAM:${streamId}] Attaching frame+status listeners to proxy`);
9037
+ proxy.on("frame", (frame) => {
9038
+ sseBrowserFrameCount++;
9039
+ if (sseBrowserFrameCount === 1) {
9040
+ console.log(`[BROWSER-STREAM:${streamId}] First browser frame received! dataSize=${frame.data?.length ?? 0} writerClosed=${writerClosed}`);
9041
+ } else if (sseBrowserFrameCount % 50 === 0) {
9042
+ console.log(`[BROWSER-STREAM:${streamId}] Browser frame #${sseBrowserFrameCount} (writerClosed=${writerClosed})`);
9043
+ }
9044
+ const rec = sessionRecorders.get(session.id);
9045
+ rec?.addFrame(frame);
9046
+ writeSSE(JSON.stringify({
9047
+ type: "browser-frame",
9048
+ data: frame.data,
9049
+ metadata: frame.metadata
9050
+ })).catch((err) => {
9051
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-frame via SSE:`, err);
9038
9052
  });
9039
- proxy.on("status", (s) => {
9040
- console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9041
- writeSSE(JSON.stringify({
9042
- type: "browser-status",
9043
- ...s
9044
- })).catch((err) => {
9045
- console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
9046
- });
9053
+ });
9054
+ proxy.on("status", (s) => {
9055
+ console.log(`[BROWSER-STREAM:${streamId}] Browser status event: connected=${s.connected} screencasting=${s.screencasting} viewport=${s.viewportWidth}x${s.viewportHeight}`);
9056
+ writeSSE(JSON.stringify({
9057
+ type: "browser-status",
9058
+ ...s
9059
+ })).catch((err) => {
9060
+ console.warn(`[BROWSER-STREAM:${streamId}] Failed to send browser-status via SSE:`, err);
9047
9061
  });
9048
- } else {
9049
- console.log(`[BROWSER-STREAM:${streamId}] Frame listeners already attached (count=${proxy.listenerCount("frame")}), skipping`);
9050
- }
9062
+ });
9063
+ proxy.requestStatus();
9051
9064
  }
9052
9065
  },
9053
9066
  onStepFinish: async () => {