browser-workspace 0.4.1 → 0.4.5

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 (117) hide show
  1. package/README.md +16 -2
  2. package/SKILL.md +60 -37
  3. package/bin/__pycache__/browser-workspacecpython-311.pyc +0 -0
  4. package/bin/browser-workspace +174 -13
  5. package/package.json +1 -1
  6. package/platform_runner.py +23 -6
  7. package/platforms/chatgpt/actions/_temporary_submit.py +22 -2
  8. package/platforms/chatgpt/tests/test_contract.py +7 -0
  9. package/platforms/wechat-channels/actions/_common.py +1 -1
  10. package/platforms/wechat-channels/actions/_post.py +37 -38
  11. package/platforms/wechat-channels/manifest.yaml +19 -13
  12. package/platforms/xhs/actions/_post.py +109 -20
  13. package/platforms/xhs/manifest.yaml +14 -5
  14. package/platforms/xhs/tests/test_contract.py +17 -0
  15. package/platforms/youtube/actions/_post.py +7 -4
  16. package/pyproject.toml +6 -2
  17. package/session_client.py +21 -11
  18. package/session_daemon.py +115 -9
  19. package/src/browser_workspace.egg-info/PKG-INFO +2 -1
  20. package/src/browser_workspace.egg-info/SOURCES.txt +84 -15
  21. package/src/browser_workspace.egg-info/entry_points.txt +2 -0
  22. package/src/browser_workspace.egg-info/requires.txt +1 -0
  23. package/src/browser_workspace.egg-info/top_level.txt +1 -1
  24. package/src/browser_workspace_app/__init__.py +1 -0
  25. package/src/browser_workspace_app/agent-workspace/agent_helpers.py +718 -0
  26. package/src/browser_workspace_app/cli.py +236 -0
  27. package/src/browser_workspace_app/extension/manifest.json +26 -0
  28. package/src/browser_workspace_app/extension/service-worker.mjs +126 -0
  29. package/src/browser_workspace_app/extension/workspace-manager.mjs +680 -0
  30. package/src/browser_workspace_app/extension/workspace-page.mjs +7 -0
  31. package/src/browser_workspace_app/extension/workspace.html +8 -0
  32. package/src/browser_workspace_app/interaction-skills/connection.md +52 -0
  33. package/src/browser_workspace_app/interaction-skills/cookies.md +3 -0
  34. package/src/browser_workspace_app/interaction-skills/cross-origin-iframes.md +3 -0
  35. package/src/browser_workspace_app/interaction-skills/dialogs.md +64 -0
  36. package/src/browser_workspace_app/interaction-skills/downloads.md +3 -0
  37. package/src/browser_workspace_app/interaction-skills/drag-and-drop.md +3 -0
  38. package/src/browser_workspace_app/interaction-skills/dropdowns.md +3 -0
  39. package/src/browser_workspace_app/interaction-skills/iframes.md +3 -0
  40. package/src/browser_workspace_app/interaction-skills/network-requests.md +3 -0
  41. package/src/browser_workspace_app/interaction-skills/print-as-pdf.md +3 -0
  42. package/src/browser_workspace_app/interaction-skills/profile-sync.md +90 -0
  43. package/src/browser_workspace_app/interaction-skills/screenshots.md +17 -0
  44. package/src/browser_workspace_app/interaction-skills/scrolling.md +22 -0
  45. package/src/browser_workspace_app/interaction-skills/shadow-dom.md +3 -0
  46. package/src/browser_workspace_app/interaction-skills/tabs.md +73 -0
  47. package/src/browser_workspace_app/interaction-skills/uploads.md +1 -0
  48. package/src/browser_workspace_app/interaction-skills/viewport.md +16 -0
  49. package/src/browser_workspace_app/licenses/browser-harness-MIT.txt +21 -0
  50. package/src/browser_workspace_app/managed_chrome.py +244 -0
  51. package/src/browser_workspace_app/node/admin.mjs +44 -0
  52. package/src/browser_workspace_app/package.json +36 -0
  53. package/src/browser_workspace_app/platform_runner.py +204 -0
  54. package/src/browser_workspace_app/platforms/chatgpt/README.md +60 -0
  55. package/src/browser_workspace_app/platforms/chatgpt/actions/_bootstrap_start.py +58 -0
  56. package/src/browser_workspace_app/platforms/chatgpt/actions/_lifecycle_common.py +119 -0
  57. package/src/browser_workspace_app/platforms/chatgpt/actions/_mcp_app.py +80 -0
  58. package/src/browser_workspace_app/platforms/chatgpt/actions/_new_thread.py +85 -0
  59. package/src/browser_workspace_app/platforms/chatgpt/actions/_new_turn.py +33 -0
  60. package/src/browser_workspace_app/platforms/chatgpt/actions/_project_setup.py +353 -0
  61. package/src/browser_workspace_app/platforms/chatgpt/actions/_readiness.py +52 -0
  62. package/src/browser_workspace_app/platforms/chatgpt/actions/_submit_thread.py +129 -0
  63. package/src/browser_workspace_app/platforms/chatgpt/actions/_temporary_submit.py +591 -0
  64. package/src/browser_workspace_app/platforms/chatgpt/actions/_thread_turn.py +362 -0
  65. package/src/browser_workspace_app/platforms/chatgpt/manifest.yaml +34 -0
  66. package/src/browser_workspace_app/platforms/chatgpt/tests/test_contract.py +183 -0
  67. package/src/browser_workspace_app/platforms/chrome-web-store/README.md +49 -0
  68. package/src/browser_workspace_app/platforms/chrome-web-store/actions/_chrome_web_store.py +425 -0
  69. package/src/browser_workspace_app/platforms/chrome-web-store/flows/check-status.md +7 -0
  70. package/src/browser_workspace_app/platforms/chrome-web-store/flows/open-item.md +10 -0
  71. package/src/browser_workspace_app/platforms/chrome-web-store/flows/submit-review.md +10 -0
  72. package/src/browser_workspace_app/platforms/chrome-web-store/flows/update-listing.md +18 -0
  73. package/src/browser_workspace_app/platforms/chrome-web-store/flows/upload-package.md +11 -0
  74. package/src/browser_workspace_app/platforms/chrome-web-store/flows/verify-published.md +9 -0
  75. package/src/browser_workspace_app/platforms/chrome-web-store/manifest.yaml +33 -0
  76. package/src/browser_workspace_app/platforms/chrome-web-store/tests/test_contract.py +83 -0
  77. package/src/browser_workspace_app/platforms/microsoft-teams/README.md +32 -0
  78. package/src/browser_workspace_app/platforms/microsoft-teams/actions/_meeting.py +210 -0
  79. package/src/browser_workspace_app/platforms/microsoft-teams/flows/instant-meeting.md +21 -0
  80. package/src/browser_workspace_app/platforms/microsoft-teams/manifest.yaml +25 -0
  81. package/src/browser_workspace_app/platforms/microsoft-teams/tests/test_contract.py +24 -0
  82. package/src/browser_workspace_app/platforms/tiktok/README.md +10 -0
  83. package/src/browser_workspace_app/platforms/tiktok/actions/_post.py +180 -0
  84. package/src/browser_workspace_app/platforms/tiktok/manifest.yaml +99 -0
  85. package/src/browser_workspace_app/platforms/wechat-channels/README.md +10 -0
  86. package/src/browser_workspace_app/platforms/wechat-channels/actions/_common.py +163 -0
  87. package/src/browser_workspace_app/platforms/wechat-channels/actions/_manage.py +130 -0
  88. package/src/browser_workspace_app/platforms/wechat-channels/actions/_post.py +231 -0
  89. package/src/browser_workspace_app/platforms/wechat-channels/manifest.yaml +124 -0
  90. package/src/browser_workspace_app/platforms/wechat-channels/tests/test_contract.py +36 -0
  91. package/src/browser_workspace_app/platforms/xhs/README.md +10 -0
  92. package/src/browser_workspace_app/platforms/xhs/actions/_manage.py +355 -0
  93. package/src/browser_workspace_app/platforms/xhs/actions/_post.py +281 -0
  94. package/src/browser_workspace_app/platforms/xhs/manifest.yaml +107 -0
  95. package/src/browser_workspace_app/platforms/xhs/tests/test_contract.py +34 -0
  96. package/src/browser_workspace_app/platforms/youtube/README.md +10 -0
  97. package/src/browser_workspace_app/platforms/youtube/actions/_post.py +103 -0
  98. package/src/browser_workspace_app/platforms/youtube/manifest.yaml +94 -0
  99. package/src/browser_workspace_app/pyproject.toml +33 -0
  100. package/src/browser_workspace_app/session_client.py +107 -0
  101. package/src/browser_workspace_app/session_daemon.py +431 -0
  102. package/src/browser_harness/__init__.py +0 -2
  103. package/src/browser_harness/__pycache__/__init__.cpython-311.pyc +0 -0
  104. package/src/browser_harness/_ipc.py +0 -201
  105. package/src/browser_harness/admin.py +0 -1588
  106. package/src/browser_harness/auth.py +0 -546
  107. package/src/browser_harness/daemon.py +0 -894
  108. package/src/browser_harness/helpers.py +0 -668
  109. package/src/browser_harness/macos.py +0 -131
  110. package/src/browser_harness/mcp_cli.py +0 -16
  111. package/src/browser_harness/paths.py +0 -49
  112. package/src/browser_harness/recorder.py +0 -343
  113. package/src/browser_harness/run.py +0 -413
  114. package/src/browser_harness/telemetry.py +0 -308
  115. package/src/browser_harness/video-template.html +0 -970
  116. package/src/browser_harness/video.py +0 -749
  117. package/src/browser_harness/video_render.py +0 -524
package/session_daemon.py CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- import ast, atexit, contextlib, io, json, os, secrets, socket, sys, threading, time, traceback
2
+ import ast, atexit, contextlib, io, json, os, secrets, signal, socket, sys, threading, time, traceback
3
3
  from pathlib import Path
4
4
 
5
5
  ROOT = Path(__file__).resolve().parent
@@ -14,18 +14,80 @@ os.environ.setdefault('BH_WORKSPACE_NAME', 'Harness')
14
14
 
15
15
  from managed_chrome import ensure_managed_chrome
16
16
  MANAGED_CHROME = ensure_managed_chrome()
17
- from browser_harness.admin import ensure_daemon
18
- ensure_daemon()
17
+ from browser_harness.admin import ensure_daemon, _process_start_time
18
+ from browser_harness import _ipc as harness_ipc
19
19
  from browser_harness import helpers as bh
20
20
 
21
+ HARNESS_OWNER_TOKEN = secrets.token_hex(24)
22
+ HARNESS_OWNER_PID = os.getpid()
23
+ HARNESS_OWNER_STARTED = _process_start_time(HARNESS_OWNER_PID)
24
+ HARNESS_OWNER_PIPE = None
25
+
26
+
27
+ def stop_owned_harness():
28
+ """Only a harness matching this daemon generation may stop."""
29
+ try:
30
+ conn, token = harness_ipc.connect(os.environ.get('BU_NAME', 'browser-workspace'), timeout=1)
31
+ with conn:
32
+ response = harness_ipc.request(conn, token, {'meta': 'owner_shutdown', 'owner_token': HARNESS_OWNER_TOKEN})
33
+ if isinstance(response, dict) and response.get('ok'):
34
+ # Allow the harness its graceful CDP/IPC cleanup before EOF of
35
+ # our owner pipe forces the crash-only exit path.
36
+ deadline = time.monotonic() + 2
37
+ while time.monotonic() < deadline and harness_ipc.ping(os.environ.get('BU_NAME', 'browser-workspace'), timeout=0.1):
38
+ time.sleep(0.05)
39
+ except (OSError, ValueError, TimeoutError):
40
+ pass
41
+
21
42
  DEFAULT_WORKSPACE = os.environ.get('BH_WORKSPACE_NAME','Harness')
22
43
  RUNTIME_DIR = Path.home()/'.config/browser-workspace/runtime'
23
44
  SOCKET_PATH = (Path(os.environ['BROWSER_WORKSPACE_SESSION_SOCKET']) if os.environ.get('BROWSER_WORKSPACE_TEST_MODE')=='1' and os.environ.get('BROWSER_WORKSPACE_SESSION_SOCKET') else RUNTIME_DIR/'session.sock')
24
- OWNER_LOCK_PATH = RUNTIME_DIR/'session-daemon.owner.lock'
45
+ OWNER_LOCK_PATH = RUNTIME_DIR/'session.lock'
25
46
  SESSIONS = {}
26
47
  SESSIONS_LOCK = threading.Lock()
27
48
  BROWSER_LOCK = threading.RLock()
28
49
 
50
+ SESSION_RECONCILE_SECONDS = 30
51
+
52
+
53
+ def is_reclaimed(info):
54
+ url = info.get('url', '')
55
+ if not url.startswith('chrome-extension://'):
56
+ return False
57
+ try:
58
+ from urllib.parse import urlparse, parse_qs
59
+ parsed = urlparse(url)
60
+ return parsed.path == '/workspace.html' and parse_qs(parsed.query).get('role') == ['idle']
61
+ except ValueError:
62
+ return False
63
+
64
+
65
+ def reconcile_sessions():
66
+ """Drop records for completely released leases, never release those tabs again."""
67
+ with BROWSER_LOCK:
68
+ try:
69
+ targets = bh.cdp('Target.getTargets')['targetInfos']
70
+ except Exception:
71
+ return []
72
+ pages = {i['targetId']: i for i in targets if i.get('type') == 'page'}
73
+ removed = []
74
+ with SESSIONS_LOCK:
75
+ for sid, session in list(SESSIONS.items()):
76
+ if session.lock.locked():
77
+ continue
78
+ # A leased root may have opened child tabs: keep any live child.
79
+ if all(tid not in pages or is_reclaimed(pages[tid])
80
+ for tid in session.owned_target_ids):
81
+ del SESSIONS[sid]
82
+ removed.append(sid)
83
+ return removed
84
+
85
+
86
+ def reconciliation_loop():
87
+ while True:
88
+ time.sleep(SESSION_RECONCILE_SECONDS)
89
+ reconcile_sessions()
90
+
29
91
 
30
92
  def compilable_module_body(body):
31
93
  """Return a module body that remains valid after trailing-result extraction.
@@ -84,10 +146,13 @@ class Session:
84
146
  return result
85
147
 
86
148
  def _cdp(self, method, **kwargs):
149
+ if method == 'Target.createTarget':
150
+ raise RuntimeError(
151
+ 'Target.createTarget is disabled in Browser Workspace sessions; '
152
+ 'use new_tab(url) so the tab is owned by the active session/workspace'
153
+ )
87
154
  result=bh.cdp(method, **kwargs)
88
- if method == 'Target.createTarget' and isinstance(result, dict):
89
- self._remember_target(result.get('targetId'))
90
- elif method == 'Target.closeTarget':
155
+ if method == 'Target.closeTarget':
91
156
  self.owned_target_ids.discard(kwargs.get('targetId'))
92
157
  return result
93
158
 
@@ -253,12 +318,31 @@ def stop_session(sid):
253
318
  if errors: release_error='; '.join(errors)
254
319
  return {'session_id':sid,'workspace':s.workspace,'target_id':s.target_id,'closed_tabs':closed_count,'release_error':release_error}
255
320
 
321
+ SOCKET_IDENTITY = None
322
+
323
+ def remove_owned_socket():
324
+ """Never unlink a socket replaced by another owner."""
325
+ if SOCKET_IDENTITY is None:
326
+ return
327
+ try:
328
+ st = SOCKET_PATH.lstat()
329
+ if (st.st_dev, st.st_ino) == SOCKET_IDENTITY:
330
+ SOCKET_PATH.unlink()
331
+ except FileNotFoundError:
332
+ pass
333
+
256
334
  def cleanup():
257
335
  with SESSIONS_LOCK: ids=list(SESSIONS)
258
336
  for sid in ids:
259
337
  try: stop_session(sid)
260
338
  except Exception: pass
261
- try: SOCKET_PATH.unlink(missing_ok=True)
339
+ stop_owned_harness()
340
+ # The write end is close-on-exec; its final close also detects SIGKILL
341
+ # and parent re-exec without relying on a recycled numeric PID.
342
+ if HARNESS_OWNER_PIPE is not None:
343
+ try: os.close(HARNESS_OWNER_PIPE)
344
+ except OSError: pass
345
+ try: remove_owned_socket()
262
346
  except Exception: pass
263
347
  atexit.register(cleanup)
264
348
 
@@ -306,6 +390,7 @@ def serve_conn(conn):
306
390
  f.write((json.dumps(result,ensure_ascii=False)+'\n').encode()); f.flush()
307
391
 
308
392
  def main():
393
+ global SOCKET_IDENTITY
309
394
  RUNTIME_DIR.mkdir(parents=True,exist_ok=True)
310
395
  try: os.chmod(RUNTIME_DIR,0o700)
311
396
  except Exception: pass
@@ -315,8 +400,29 @@ def main():
315
400
  fcntl.flock(owner,fcntl.LOCK_EX|fcntl.LOCK_NB)
316
401
  except BlockingIOError:
317
402
  owner.close(); return
403
+ global HARNESS_OWNER_PIPE
404
+ read_fd, write_fd = os.pipe() if os.name == 'posix' else (None, None)
405
+ HARNESS_OWNER_PIPE = write_fd
406
+ try:
407
+ owner_env = {
408
+ 'BH_OWNER_PID': str(HARNESS_OWNER_PID),
409
+ 'BH_OWNER_STARTED': str(HARNESS_OWNER_STARTED or ''),
410
+ 'BH_OWNER_TOKEN': HARNESS_OWNER_TOKEN,
411
+ }
412
+ if read_fd is not None:
413
+ owner_env['BH_OWNER_PIPE_FD'] = str(read_fd)
414
+ ensure_daemon(env=owner_env)
415
+ finally:
416
+ if read_fd is not None:
417
+ os.close(read_fd)
418
+ def terminate(_signum, _frame):
419
+ raise SystemExit(0)
420
+ signal.signal(signal.SIGTERM, terminate)
421
+ signal.signal(signal.SIGINT, terminate)
422
+ # Lock ownership is required before changing the shared socket path.
318
423
  SOCKET_PATH.unlink(missing_ok=True)
319
- srv=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); srv.bind(str(SOCKET_PATH)); os.chmod(SOCKET_PATH,0o600); srv.listen(32)
424
+ srv=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM); srv.bind(str(SOCKET_PATH)); os.chmod(SOCKET_PATH,0o600); SOCKET_IDENTITY=(SOCKET_PATH.stat().st_dev, SOCKET_PATH.stat().st_ino); srv.listen(32)
425
+ threading.Thread(target=reconciliation_loop, daemon=True).start()
320
426
  try:
321
427
  while True:
322
428
  conn,_=srv.accept(); threading.Thread(target=serve_conn,args=(conn,),daemon=True).start()
@@ -1,8 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: browser-workspace
3
- Version: 0.4.0
3
+ Version: 0.4.5
4
4
  Summary: Agent browser workspace with persistent Python sessions, tab leasing, snapshots, and platform workflows.
5
5
  Requires-Python: >=3.11
6
+ Requires-Dist: browser-harness @ git+https://github.com/lalalic/browser-harness.git@80b74e116b9126f5159c84e5e05a750573e5f192
6
7
  Requires-Dist: cdp-use==1.4.5
7
8
  Requires-Dist: fetch-use==0.4.0
8
9
  Requires-Dist: pillow==12.3.0
@@ -1,28 +1,97 @@
1
1
  README.md
2
2
  pyproject.toml
3
- src/browser_harness/__init__.py
4
- src/browser_harness/_ipc.py
5
- src/browser_harness/admin.py
6
- src/browser_harness/auth.py
7
- src/browser_harness/daemon.py
8
- src/browser_harness/helpers.py
9
- src/browser_harness/macos.py
10
- src/browser_harness/mcp_cli.py
11
- src/browser_harness/paths.py
12
- src/browser_harness/recorder.py
13
- src/browser_harness/run.py
14
- src/browser_harness/telemetry.py
15
- src/browser_harness/video-template.html
16
- src/browser_harness/video.py
17
- src/browser_harness/video_render.py
18
3
  src/browser_workspace.egg-info/PKG-INFO
19
4
  src/browser_workspace.egg-info/SOURCES.txt
20
5
  src/browser_workspace.egg-info/dependency_links.txt
6
+ src/browser_workspace.egg-info/entry_points.txt
21
7
  src/browser_workspace.egg-info/requires.txt
22
8
  src/browser_workspace.egg-info/top_level.txt
9
+ src/browser_workspace_app/__init__.py
10
+ src/browser_workspace_app/cli.py
11
+ src/browser_workspace_app/managed_chrome.py
12
+ src/browser_workspace_app/package.json
13
+ src/browser_workspace_app/platform_runner.py
14
+ src/browser_workspace_app/pyproject.toml
15
+ src/browser_workspace_app/session_client.py
16
+ src/browser_workspace_app/session_daemon.py
17
+ src/browser_workspace_app/agent-workspace/agent_helpers.py
18
+ src/browser_workspace_app/extension/manifest.json
19
+ src/browser_workspace_app/extension/service-worker.mjs
20
+ src/browser_workspace_app/extension/workspace-manager.mjs
21
+ src/browser_workspace_app/extension/workspace-page.mjs
22
+ src/browser_workspace_app/extension/workspace.html
23
+ src/browser_workspace_app/interaction-skills/connection.md
24
+ src/browser_workspace_app/interaction-skills/cookies.md
25
+ src/browser_workspace_app/interaction-skills/cross-origin-iframes.md
26
+ src/browser_workspace_app/interaction-skills/dialogs.md
27
+ src/browser_workspace_app/interaction-skills/downloads.md
28
+ src/browser_workspace_app/interaction-skills/drag-and-drop.md
29
+ src/browser_workspace_app/interaction-skills/dropdowns.md
30
+ src/browser_workspace_app/interaction-skills/iframes.md
31
+ src/browser_workspace_app/interaction-skills/network-requests.md
32
+ src/browser_workspace_app/interaction-skills/print-as-pdf.md
33
+ src/browser_workspace_app/interaction-skills/profile-sync.md
34
+ src/browser_workspace_app/interaction-skills/screenshots.md
35
+ src/browser_workspace_app/interaction-skills/scrolling.md
36
+ src/browser_workspace_app/interaction-skills/shadow-dom.md
37
+ src/browser_workspace_app/interaction-skills/tabs.md
38
+ src/browser_workspace_app/interaction-skills/uploads.md
39
+ src/browser_workspace_app/interaction-skills/viewport.md
40
+ src/browser_workspace_app/licenses/browser-harness-MIT.txt
41
+ src/browser_workspace_app/node/admin.mjs
42
+ src/browser_workspace_app/platforms/chatgpt/README.md
43
+ src/browser_workspace_app/platforms/chatgpt/manifest.yaml
44
+ src/browser_workspace_app/platforms/chatgpt/actions/_bootstrap_start.py
45
+ src/browser_workspace_app/platforms/chatgpt/actions/_lifecycle_common.py
46
+ src/browser_workspace_app/platforms/chatgpt/actions/_mcp_app.py
47
+ src/browser_workspace_app/platforms/chatgpt/actions/_new_thread.py
48
+ src/browser_workspace_app/platforms/chatgpt/actions/_new_turn.py
49
+ src/browser_workspace_app/platforms/chatgpt/actions/_project_setup.py
50
+ src/browser_workspace_app/platforms/chatgpt/actions/_readiness.py
51
+ src/browser_workspace_app/platforms/chatgpt/actions/_submit_thread.py
52
+ src/browser_workspace_app/platforms/chatgpt/actions/_temporary_submit.py
53
+ src/browser_workspace_app/platforms/chatgpt/actions/_thread_turn.py
54
+ src/browser_workspace_app/platforms/chatgpt/tests/test_contract.py
55
+ src/browser_workspace_app/platforms/chrome-web-store/README.md
56
+ src/browser_workspace_app/platforms/chrome-web-store/manifest.yaml
57
+ src/browser_workspace_app/platforms/chrome-web-store/actions/_chrome_web_store.py
58
+ src/browser_workspace_app/platforms/chrome-web-store/flows/check-status.md
59
+ src/browser_workspace_app/platforms/chrome-web-store/flows/open-item.md
60
+ src/browser_workspace_app/platforms/chrome-web-store/flows/submit-review.md
61
+ src/browser_workspace_app/platforms/chrome-web-store/flows/update-listing.md
62
+ src/browser_workspace_app/platforms/chrome-web-store/flows/upload-package.md
63
+ src/browser_workspace_app/platforms/chrome-web-store/flows/verify-published.md
64
+ src/browser_workspace_app/platforms/chrome-web-store/tests/test_contract.py
65
+ src/browser_workspace_app/platforms/microsoft-teams/README.md
66
+ src/browser_workspace_app/platforms/microsoft-teams/manifest.yaml
67
+ src/browser_workspace_app/platforms/microsoft-teams/actions/_meeting.py
68
+ src/browser_workspace_app/platforms/microsoft-teams/flows/instant-meeting.md
69
+ src/browser_workspace_app/platforms/microsoft-teams/tests/test_contract.py
70
+ src/browser_workspace_app/platforms/tiktok/README.md
71
+ src/browser_workspace_app/platforms/tiktok/manifest.yaml
72
+ src/browser_workspace_app/platforms/tiktok/actions/_post.py
73
+ src/browser_workspace_app/platforms/wechat-channels/README.md
74
+ src/browser_workspace_app/platforms/wechat-channels/manifest.yaml
75
+ src/browser_workspace_app/platforms/wechat-channels/actions/_common.py
76
+ src/browser_workspace_app/platforms/wechat-channels/actions/_manage.py
77
+ src/browser_workspace_app/platforms/wechat-channels/actions/_post.py
78
+ src/browser_workspace_app/platforms/wechat-channels/tests/test_contract.py
79
+ src/browser_workspace_app/platforms/xhs/README.md
80
+ src/browser_workspace_app/platforms/xhs/manifest.yaml
81
+ src/browser_workspace_app/platforms/xhs/actions/_manage.py
82
+ src/browser_workspace_app/platforms/xhs/actions/_post.py
83
+ src/browser_workspace_app/platforms/xhs/tests/test_contract.py
84
+ src/browser_workspace_app/platforms/youtube/README.md
85
+ src/browser_workspace_app/platforms/youtube/manifest.yaml
86
+ src/browser_workspace_app/platforms/youtube/actions/_post.py
87
+ test/test_auth_cookie_audit.py
88
+ test/test_auth_keepalive_visit.py
89
+ test/test_harness_ownership.py
23
90
  test/test_managed_chrome.py
24
91
  test/test_platform_profile.py
25
92
  test/test_platform_runner.py
93
+ test/test_pypi_package.py
26
94
  test/test_session_cli.py
27
95
  test/test_session_execution.py
96
+ test/test_session_reconcile.py
28
97
  test/test_workspace_cli.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ browser-workspace = browser_workspace_app.cli:main
@@ -1,3 +1,4 @@
1
+ browser-harness @ git+https://github.com/lalalic/browser-harness.git@80b74e116b9126f5159c84e5e05a750573e5f192
1
2
  cdp-use==1.4.5
2
3
  fetch-use==0.4.0
3
4
  pillow==12.3.0
@@ -1 +1 @@
1
- browser_harness
1
+ browser_workspace_app
@@ -0,0 +1 @@
1
+ """Browser Workspace Python distribution."""