Skip to content

Commit 3f99e7f

Browse files
committed
Separate deploy entrypoints from local runners and share lifecycle logging helpers.
Remove direct-run fallbacks from deployable app entrypoints and centralize duplicated browser lifecycle backend/session event emission in reusable Python and TypeScript logging helpers. Made-with: Cursor
1 parent c76d318 commit 3f99e7f

6 files changed

Lines changed: 155 additions & 141 deletions

File tree

pkg/templates/python/openai-computer-use/agent/logging.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,54 @@ def render_text(event: dict) -> None:
283283
sys.stderr.flush()
284284

285285
return render_text
286+
287+
288+
def emit_browser_new_started(on_event: Callable[[dict], None]) -> None:
289+
on_event({"event": "backend", "data": {"op": "browsers.new"}})
290+
291+
292+
def emit_browser_new_done(
293+
on_event: Callable[[dict], None], started_at: datetime, live_view_url: str | None
294+
) -> None:
295+
on_event(
296+
{
297+
"event": "backend",
298+
"data": {
299+
"op": "browsers.new.done",
300+
"detail": live_view_url or "",
301+
"elapsed_ms": int((datetime.now() - started_at).total_seconds() * 1000),
302+
},
303+
}
304+
)
305+
306+
307+
def emit_session_state(
308+
on_event: Callable[[dict], None], session_id: str, live_view_url: str | None
309+
) -> None:
310+
on_event(
311+
{
312+
"event": "session_state",
313+
"data": {
314+
"session_id": session_id,
315+
"live_view_url": live_view_url or "",
316+
},
317+
}
318+
)
319+
320+
321+
def emit_browser_delete_started(on_event: Callable[[dict], None]) -> None:
322+
on_event({"event": "backend", "data": {"op": "browsers.delete"}})
323+
324+
325+
def emit_browser_delete_done(
326+
on_event: Callable[[dict], None], started_at: datetime
327+
) -> None:
328+
on_event(
329+
{
330+
"event": "backend",
331+
"data": {
332+
"op": "browsers.delete.done",
333+
"elapsed_ms": int((datetime.now() - started_at).total_seconds() * 1000),
334+
},
335+
}
336+
)

pkg/templates/python/openai-computer-use/main.py

Lines changed: 17 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import asyncio
22
import datetime
33
import os
4-
import subprocess
5-
import sys
64
from typing import NotRequired, TypedDict
75

86
import kernel
97
from agent import Agent
10-
from agent.logging import create_event_logger
8+
from agent.logging import (
9+
create_event_logger,
10+
emit_browser_delete_done,
11+
emit_browser_delete_started,
12+
emit_browser_new_done,
13+
emit_browser_new_started,
14+
emit_session_state,
15+
)
1116
from computers.kernel_computer import KernelComputer
1217
from kernel import Kernel
1318

@@ -56,31 +61,15 @@ async def cua_task(
5661
on_event = create_event_logger(output=output_mode)
5762

5863
browser_create_started_at = datetime.datetime.now()
59-
on_event({"event": "backend", "data": {"op": "browsers.new"}})
64+
emit_browser_new_started(on_event)
6065
kernel_browser = await asyncio.to_thread(
6166
client.browsers.create, invocation_id=ctx.invocation_id, stealth=True
6267
)
63-
on_event(
64-
{
65-
"event": "backend",
66-
"data": {
67-
"op": "browsers.new.done",
68-
"detail": kernel_browser.browser_live_view_url or "",
69-
"elapsed_ms": int(
70-
(datetime.datetime.now() - browser_create_started_at).total_seconds()
71-
* 1000
72-
),
73-
},
74-
}
68+
emit_browser_new_done(
69+
on_event, browser_create_started_at, kernel_browser.browser_live_view_url
7570
)
76-
on_event(
77-
{
78-
"event": "session_state",
79-
"data": {
80-
"session_id": kernel_browser.session_id,
81-
"live_view_url": kernel_browser.browser_live_view_url or "",
82-
},
83-
}
71+
emit_session_state(
72+
on_event, kernel_browser.session_id, kernel_browser.browser_live_view_url
8473
)
8574

8675
def run_agent():
@@ -133,26 +122,10 @@ def run_agent():
133122
return await asyncio.to_thread(run_agent)
134123
finally:
135124
browser_delete_started_at = datetime.datetime.now()
136-
on_event({"event": "backend", "data": {"op": "browsers.delete"}})
125+
emit_browser_delete_started(on_event)
137126
try:
138127
await asyncio.to_thread(client.browsers.delete_by_id, kernel_browser.session_id)
139128
finally:
140-
on_event(
141-
{
142-
"event": "backend",
143-
"data": {
144-
"op": "browsers.delete.done",
145-
"elapsed_ms": int(
146-
(datetime.datetime.now() - browser_delete_started_at).total_seconds()
147-
* 1000
148-
),
149-
},
150-
}
151-
)
152-
153-
154-
if __name__ == "__main__":
155-
# `main.py` is the deployable Kernel app entrypoint.
156-
# For local execution, forward to the existing local harness.
157-
command = [sys.executable, "run_local.py", *sys.argv[1:]]
158-
raise SystemExit(subprocess.call(command))
129+
emit_browser_delete_done(on_event, browser_delete_started_at)
130+
131+

pkg/templates/python/openai-computer-use/run_local.py

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@
1616

1717
from kernel import Kernel
1818
from agent import Agent
19-
from agent.logging import create_event_logger
19+
from agent.logging import (
20+
create_event_logger,
21+
emit_browser_delete_done,
22+
emit_browser_delete_started,
23+
emit_browser_new_done,
24+
emit_browser_new_started,
25+
emit_session_state,
26+
)
2027
from computers.kernel_computer import KernelComputer
2128

2229

@@ -47,30 +54,12 @@ def main():
4754
on_event = create_event_logger(output=args.output, verbose=args.debug)
4855

4956
browser_create_started_at = datetime.datetime.now()
50-
on_event({"event": "backend", "data": {"op": "browsers.new"}})
57+
emit_browser_new_started(on_event)
5158
browser = client.browsers.create(timeout_seconds=300)
52-
on_event(
53-
{
54-
"event": "backend",
55-
"data": {
56-
"op": "browsers.new.done",
57-
"detail": browser.browser_live_view_url or "",
58-
"elapsed_ms": int(
59-
(datetime.datetime.now() - browser_create_started_at).total_seconds()
60-
* 1000
61-
),
62-
},
63-
}
64-
)
65-
on_event(
66-
{
67-
"event": "session_state",
68-
"data": {
69-
"session_id": browser.session_id,
70-
"live_view_url": browser.browser_live_view_url or "",
71-
},
72-
}
59+
emit_browser_new_done(
60+
on_event, browser_create_started_at, browser.browser_live_view_url
7361
)
62+
emit_session_state(on_event, browser.session_id, browser.browser_live_view_url)
7463

7564
computer = KernelComputer(client, browser.session_id, on_event=on_event)
7665

@@ -108,22 +97,11 @@ def main():
10897
raise ValueError("No response from agent")
10998
finally:
11099
browser_delete_started_at = datetime.datetime.now()
111-
on_event({"event": "backend", "data": {"op": "browsers.delete"}})
100+
emit_browser_delete_started(on_event)
112101
try:
113102
client.browsers.delete_by_id(browser.session_id)
114103
finally:
115-
on_event(
116-
{
117-
"event": "backend",
118-
"data": {
119-
"op": "browsers.delete.done",
120-
"elapsed_ms": int(
121-
(datetime.datetime.now() - browser_delete_started_at).total_seconds()
122-
* 1000
123-
),
124-
},
125-
}
126-
)
104+
emit_browser_delete_done(on_event, browser_delete_started_at)
127105
print("> Browser session deleted")
128106

129107

pkg/templates/typescript/openai-computer-use/index.ts

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { Kernel, type KernelContext } from '@onkernel/sdk';
22
import * as dotenv from 'dotenv';
3-
import { resolve } from 'node:path';
4-
import { fileURLToPath } from 'node:url';
53
import type { ResponseItem, ResponseOutputMessage } from 'openai/resources/responses/responses';
64
import { Agent } from './lib/agent';
75
import { KernelComputer } from './lib/kernel-computer';
8-
import { createEventLogger } from './lib/logging';
6+
import {
7+
createEventLogger,
8+
emitBrowserDeleteDone,
9+
emitBrowserDeleteStarted,
10+
emitBrowserNewDone,
11+
emitBrowserNewStarted,
12+
emitSessionState,
13+
} from './lib/logging';
914
import type { OutputMode } from './lib/log-events';
1015

1116
dotenv.config({ override: true, quiet: true });
@@ -48,21 +53,11 @@ app.action<CuaInput, CuaOutput>(
4853
const outputMode: OutputMode = payload.output === 'jsonl' ? 'jsonl' : 'text';
4954
const onEvent = createEventLogger({ output: outputMode });
5055

51-
onEvent({ event: 'backend', data: { op: 'browsers.new' } });
56+
emitBrowserNewStarted(onEvent);
5257
const browserCreateStartedAt = Date.now();
5358
const kb = await kernel.browsers.create({ invocation_id: ctx.invocation_id });
54-
onEvent({
55-
event: 'backend',
56-
data: {
57-
op: 'browsers.new.done',
58-
detail: kb.browser_live_view_url ?? '',
59-
elapsed_ms: Date.now() - browserCreateStartedAt,
60-
},
61-
});
62-
onEvent({
63-
event: 'session_state',
64-
data: { session_id: kb.session_id, live_view_url: kb.browser_live_view_url ?? '' },
65-
});
59+
emitBrowserNewDone(onEvent, browserCreateStartedAt, kb.browser_live_view_url);
60+
emitSessionState(onEvent, kb.session_id, kb.browser_live_view_url);
6661

6762
const computer = new KernelComputer(kernel, kb.session_id, onEvent);
6863

@@ -119,34 +114,13 @@ app.action<CuaInput, CuaOutput>(
119114
console.error('Error in cua-task:', error);
120115
return { elapsed, answer: null };
121116
} finally {
122-
onEvent({ event: 'backend', data: { op: 'browsers.delete' } });
117+
emitBrowserDeleteStarted(onEvent);
123118
const browserDeleteStartedAt = Date.now();
124119
try {
125120
await kernel.browsers.deleteByID(kb.session_id);
126121
} finally {
127-
onEvent({
128-
event: 'backend',
129-
data: {
130-
op: 'browsers.delete.done',
131-
elapsed_ms: Date.now() - browserDeleteStartedAt,
132-
},
133-
});
122+
emitBrowserDeleteDone(onEvent, browserDeleteStartedAt);
134123
}
135124
}
136125
},
137126
);
138-
139-
function isDirectRun(): boolean {
140-
const entry = process.argv[1];
141-
if (!entry) return false;
142-
return resolve(entry) === resolve(fileURLToPath(import.meta.url));
143-
}
144-
145-
if (isDirectRun()) {
146-
void import('./run_local')
147-
.then(({ runLocalTest }) => runLocalTest(process.argv.slice(2)))
148-
.catch((error: unknown) => {
149-
console.error(error);
150-
process.exit(1);
151-
});
152-
}

pkg/templates/typescript/openai-computer-use/lib/logging.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,53 @@ function formatKernelOp(op: string): string {
2525
return `${op}()`;
2626
}
2727

28+
export function emitBrowserNewStarted(onEvent: (event: AgentEvent) => void): void {
29+
onEvent({ event: 'backend', data: { op: 'browsers.new' } });
30+
}
31+
32+
export function emitBrowserNewDone(
33+
onEvent: (event: AgentEvent) => void,
34+
startedAtMs: number,
35+
liveViewUrl?: string | null,
36+
): void {
37+
onEvent({
38+
event: 'backend',
39+
data: {
40+
op: 'browsers.new.done',
41+
detail: liveViewUrl ?? '',
42+
elapsed_ms: Date.now() - startedAtMs,
43+
},
44+
});
45+
}
46+
47+
export function emitSessionState(
48+
onEvent: (event: AgentEvent) => void,
49+
sessionId: string,
50+
liveViewUrl?: string | null,
51+
): void {
52+
onEvent({
53+
event: 'session_state',
54+
data: { session_id: sessionId, live_view_url: liveViewUrl ?? '' },
55+
});
56+
}
57+
58+
export function emitBrowserDeleteStarted(onEvent: (event: AgentEvent) => void): void {
59+
onEvent({ event: 'backend', data: { op: 'browsers.delete' } });
60+
}
61+
62+
export function emitBrowserDeleteDone(
63+
onEvent: (event: AgentEvent) => void,
64+
startedAtMs: number,
65+
): void {
66+
onEvent({
67+
event: 'backend',
68+
data: {
69+
op: 'browsers.delete.done',
70+
elapsed_ms: Date.now() - startedAtMs,
71+
},
72+
});
73+
}
74+
2875
class ThinkingSpinner {
2976
private active = false;
3077
private timer: NodeJS.Timeout | null = null;

0 commit comments

Comments
 (0)