python-codex 0.3.3__py3-none-any.whl → 0.4.1__py3-none-any.whl

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 (61) hide show
  1. pycodex/__init__.py +3 -27
  2. pycodex/bootstrap.py +5 -45
  3. pycodex/cli.py +1 -14
  4. pycodex/context.py +1 -1
  5. pycodex/events.py +1 -1
  6. pycodex/model.py +2 -2
  7. pycodex/portable/__init__.py +45 -0
  8. pycodex/{portable.py → portable/client.py} +4 -7
  9. pycodex/{portable_server.py → portable/server.py} +3 -3
  10. pycodex/protocol.py +1 -1
  11. pycodex/runtime.py +2 -2
  12. pycodex/tools/base_tool.py +1 -1
  13. pycodex/tools/close_agent_tool.py +1 -1
  14. pycodex/tools/code_mode_manager.py +1 -1
  15. pycodex/tools/request_permissions_tool.py +1 -1
  16. pycodex/tools/resume_agent_tool.py +1 -1
  17. pycodex/{runtime_services.py → tools/runtime_services.py} +5 -5
  18. pycodex/tools/send_input_tool.py +1 -1
  19. pycodex/tools/spawn_agent_tool.py +1 -1
  20. pycodex/tools/unified_exec_manager.py +1 -1
  21. pycodex/tools/update_plan_tool.py +1 -1
  22. pycodex/tools/view_image_tool.py +5 -0
  23. pycodex/tools/wait_agent_tool.py +1 -1
  24. pycodex/utils/__init__.py +0 -4
  25. pycodex/utils/_aes_gcm/LICENSE +56 -0
  26. pycodex/utils/_aes_gcm/README.md +61 -0
  27. pycodex/utils/_aes_gcm/__init__.py +1 -0
  28. pycodex/utils/_aes_gcm/aes.py +175 -0
  29. pycodex/utils/_aes_gcm/gcm.py +73 -0
  30. pycodex/utils/_aes_gcm/source-sha256.json +4 -0
  31. pycodex/utils/async_bridge.py +3 -4
  32. pycodex/{compat.py → utils/compat.py} +2 -0
  33. pycodex/utils/crypto.py +35 -0
  34. pycodex/utils/debug.py +24 -0
  35. pycodex/{doctor.py → utils/doctor.py} +3 -3
  36. pycodex/utils/image_utils.py +3 -2
  37. pycodex/{model_metadata.py → utils/model_metadata.py} +2 -2
  38. pycodex/utils/session_persist.py +28 -0
  39. {python_codex-0.3.3.dist-info → python_codex-0.4.1.dist-info}/METADATA +124 -42
  40. python_codex-0.4.1.dist-info/RECORD +90 -0
  41. {python_codex-0.3.3.dist-info → python_codex-0.4.1.dist-info}/entry_points.txt +0 -1
  42. responses_server/app.py +28 -15
  43. pycodex/feishu_card.py +0 -793
  44. pycodex/feishu_link.py +0 -440
  45. pycodex/tools/ipython_tool.py +0 -142
  46. python_codex-0.3.3.dist-info/RECORD +0 -98
  47. workspace_server/__init__.py +0 -39
  48. workspace_server/__main__.py +0 -4
  49. workspace_server/app.py +0 -1906
  50. workspace_server/content_views.LICENSE.txt +0 -470
  51. workspace_server/content_views.css +0 -1
  52. workspace_server/content_views.mjs +0 -25
  53. workspace_server/editor.css +0 -1
  54. workspace_server/editor.mjs +0 -1260
  55. workspace_server/owned_process.py +0 -27
  56. workspace_server/workspace.html +0 -2267
  57. workspace_server/workspace_views.py +0 -533
  58. workspace_server/workspaces.html +0 -550
  59. workspace_server/workspaces.py +0 -632
  60. {python_codex-0.3.3.dist-info → python_codex-0.4.1.dist-info}/WHEEL +0 -0
  61. {python_codex-0.3.3.dist-info → python_codex-0.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,550 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>pycodex workspaces</title>
7
- <style>
8
- :root {
9
- color-scheme: light dark;
10
- --bg: #f6f7f9;
11
- --panel: #ffffff;
12
- --panel-soft: #f0f3f6;
13
- --text: #17202a;
14
- --muted: #687483;
15
- --line: #dbe1e8;
16
- --accent: #0b6bcb;
17
- --accent-strong: #0859a8;
18
- --danger: #b42318;
19
- --danger-soft: #fff0ed;
20
- font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
21
- }
22
- @media (prefers-color-scheme: dark) {
23
- :root {
24
- --bg: #101418;
25
- --panel: #171d23;
26
- --panel-soft: #202832;
27
- --text: #eef3f8;
28
- --muted: #9aa7b5;
29
- --line: #2d3844;
30
- --accent: #5aa9ff;
31
- --accent-strong: #83bdff;
32
- --danger: #ff8c7f;
33
- --danger-soft: #321d1b;
34
- }
35
- }
36
- * {
37
- box-sizing: border-box;
38
- }
39
- body {
40
- margin: 0;
41
- min-height: 100vh;
42
- background: var(--bg);
43
- color: var(--text);
44
- font-size: 14px;
45
- }
46
- button, input {
47
- font: inherit;
48
- }
49
- button, a.button {
50
- display: inline-flex;
51
- min-height: 38px;
52
- align-items: center;
53
- justify-content: center;
54
- gap: 7px;
55
- border: 1px solid var(--line);
56
- border-radius: 9px;
57
- padding: 8px 14px;
58
- background: var(--panel);
59
- color: var(--text);
60
- text-decoration: none;
61
- cursor: pointer;
62
- white-space: nowrap;
63
- }
64
- button.primary {
65
- border-color: var(--accent);
66
- background: var(--accent);
67
- color: #fff;
68
- }
69
- button.primary:hover {
70
- background: var(--accent-strong);
71
- }
72
- button.danger {
73
- border-color: transparent;
74
- background: transparent;
75
- color: var(--muted);
76
- }
77
- button.danger:hover {
78
- background: var(--danger-soft);
79
- color: var(--danger);
80
- }
81
- button:disabled {
82
- cursor: default;
83
- opacity: .62;
84
- }
85
- input {
86
- width: 100%;
87
- min-height: 38px;
88
- border: 1px solid var(--line);
89
- border-radius: 9px;
90
- padding: 8px 10px;
91
- background: var(--panel);
92
- color: var(--text);
93
- outline: none;
94
- }
95
- input:focus {
96
- border-color: var(--accent);
97
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
98
- }
99
- input::placeholder {
100
- color: var(--muted);
101
- }
102
- .page {
103
- width: min(1180px, calc(100vw - 48px));
104
- margin: 0 auto;
105
- padding: 28px 0 40px;
106
- }
107
- .topbar {
108
- display: flex;
109
- align-items: center;
110
- justify-content: space-between;
111
- gap: 16px;
112
- margin-bottom: 24px;
113
- }
114
- .brand {
115
- min-width: 0;
116
- }
117
- .brand h1 {
118
- margin: 0;
119
- font-size: 28px;
120
- line-height: 1.25;
121
- font-weight: 600;
122
- letter-spacing: -0.8px;
123
- }
124
- .eyebrow { margin-bottom: 6px; color: var(--accent); font-size: 13px; font-weight: 600; }
125
- .brand p {
126
- margin: 10px 0 0;
127
- color: var(--muted);
128
- line-height: 1.45;
129
- }
130
- .status {
131
- min-width: 220px;
132
- max-width: 420px;
133
- color: var(--muted);
134
- text-align: right;
135
- line-height: 1.4;
136
- }
137
- .status.error {
138
- color: var(--danger);
139
- }
140
- .layout {
141
- display: grid;
142
- grid-template-columns: minmax(0, 1fr) 300px;
143
- gap: 28px;
144
- align-items: start;
145
- }
146
- .panel {
147
- min-width: 0;
148
- }
149
- .create-panel { background: var(--panel); border: 1px solid var(--line); border-radius: 14px; }
150
- .create-panel .panel-head { border-bottom: 0; padding-bottom: 0; }
151
- .panel-head {
152
- display: flex;
153
- align-items: center;
154
- justify-content: space-between;
155
- gap: 12px;
156
- border-bottom: 1px solid var(--line);
157
- padding: 12px 0;
158
- }
159
- .create-panel .panel-head { padding: 20px 20px 0; }
160
- .panel-title {
161
- margin: 0;
162
- font-size: 17px;
163
- font-weight: 600;
164
- }
165
- .panel-subtitle {
166
- margin: 8px 0 0;
167
- color: var(--muted);
168
- font-size: 13px;
169
- line-height: 1.6;
170
- }
171
- form {
172
- display: grid;
173
- gap: 14px;
174
- padding: 20px;
175
- }
176
- label {
177
- display: grid;
178
- gap: 8px;
179
- color: var(--text);
180
- font-size: 13px;
181
- font-weight: 500;
182
- }
183
- label span {
184
- color: var(--muted);
185
- }
186
- .form-actions {
187
- display: flex;
188
- gap: 9px;
189
- align-items: center;
190
- justify-content: flex-end;
191
- margin-top: 2px;
192
- }
193
- .form-actions button { width: 100%; }
194
- .summary {
195
- display: flex;
196
- gap: 24px;
197
- padding: 12px 0;
198
- border-bottom: 1px solid var(--line);
199
- }
200
- .metric {
201
- min-width: 0;
202
- display: flex;
203
- align-items: baseline;
204
- gap: 6px;
205
- }
206
- .metric-value {
207
- display: block;
208
- font-size: 14px;
209
- font-weight: 600;
210
- line-height: 1.1;
211
- }
212
- .metric-label {
213
- display: block;
214
- color: var(--muted);
215
- font-size: 12px;
216
- }
217
- .workspace-list {
218
- display: grid;
219
- }
220
- .workspace-row {
221
- display: grid;
222
- grid-template-columns: minmax(0, 1fr) auto;
223
- gap: 8px 20px;
224
- align-items: center;
225
- border-bottom: 1px solid var(--line);
226
- padding: 16px 0;
227
- }
228
- .workspace-name {
229
- min-width: 0;
230
- }
231
- .workspace-name, .paths { grid-column: 1; }
232
- .workspace-name strong {
233
- display: block;
234
- overflow: hidden;
235
- text-overflow: ellipsis;
236
- white-space: nowrap;
237
- font-size: 16px;
238
- font-weight: 600;
239
- }
240
- .session-count {
241
- display: inline-flex;
242
- margin-top: 6px;
243
- color: var(--muted);
244
- font-size: 12px;
245
- }
246
- .paths {
247
- display: grid;
248
- gap: 6px;
249
- min-width: 0;
250
- }
251
- .path-line {
252
- display: grid;
253
- grid-template-columns: 64px minmax(0, 1fr);
254
- gap: 8px;
255
- align-items: baseline;
256
- min-width: 0;
257
- }
258
- .path-label {
259
- color: var(--muted);
260
- font-size: 12px;
261
- font-weight: 620;
262
- }
263
- .path-value {
264
- overflow: hidden;
265
- text-overflow: ellipsis;
266
- white-space: nowrap;
267
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
268
- font-size: 12px;
269
- }
270
- .actions {
271
- display: flex;
272
- gap: 8px;
273
- justify-content: flex-end;
274
- flex-wrap: wrap;
275
- grid-column: 2;
276
- grid-row: 1 / span 2;
277
- }
278
- .actions a.button { color: var(--accent); border-color: var(--line); }
279
- .actions a.button:hover { background: var(--panel-soft); }
280
- .empty {
281
- display: grid;
282
- justify-items: center;
283
- gap: 8px;
284
- padding: 54px 18px;
285
- color: var(--muted);
286
- text-align: center;
287
- }
288
- .empty strong {
289
- color: var(--text);
290
- font-size: 15px;
291
- }
292
- @media (max-width: 860px) {
293
- .page {
294
- width: min(100vw - 40px, 720px);
295
- padding-top: 28px;
296
- }
297
- .topbar {
298
- align-items: flex-start;
299
- flex-direction: column;
300
- }
301
- .status {
302
- min-width: 0;
303
- text-align: left;
304
- }
305
- .layout {
306
- grid-template-columns: 1fr;
307
- }
308
- .workspace-row {
309
- align-items: stretch;
310
- }
311
- .actions {
312
- justify-content: flex-start;
313
- }
314
- }
315
- @media (max-width: 520px) {
316
- .brand h1 { font-size: 28px; }
317
- .topbar { margin-bottom: 24px; }
318
- .layout { gap: 32px; }
319
- .summary { gap: 16px; }
320
- .workspace-row { grid-template-columns: 1fr; }
321
- .actions { grid-column: 1; grid-row: auto; }
322
- .path-line {
323
- grid-template-columns: 1fr;
324
- }
325
- .path-value {
326
- white-space: normal;
327
- overflow-wrap: anywhere;
328
- }
329
- }
330
- </style>
331
- </head>
332
- <body>
333
- <main class="page">
334
- <header class="topbar">
335
- <div class="brand">
336
- <div class="eyebrow">pycodex</div>
337
- <h1>Your workspaces</h1>
338
- <p>A place for each project. Pick up where you left off.</p>
339
- </div>
340
- <div id="status" class="status" role="status"></div>
341
- </header>
342
-
343
- <div class="layout">
344
- <section class="panel" aria-labelledby="listTitle">
345
- <div class="panel-head">
346
- <h2 id="listTitle" class="panel-title">Workspaces</h2>
347
- <button id="refreshButton" type="button">Refresh</button>
348
- </div>
349
- <div class="summary" aria-label="Workspace summary">
350
- <div class="metric">
351
- <span id="workspaceCount" class="metric-value">0</span>
352
- <span class="metric-label">workspaces</span>
353
- </div>
354
- <div class="metric">
355
- <span id="sessionCount" class="metric-value">0</span>
356
- <span class="metric-label">sessions</span>
357
- </div>
358
- <div class="metric">
359
- <span id="activeCount" class="metric-value">0</span>
360
- <span class="metric-label">running</span>
361
- </div>
362
- </div>
363
- <div id="workspaceRows" class="workspace-list"></div>
364
- </section>
365
- <section class="panel create-panel" aria-labelledby="addTitle">
366
- <div class="panel-head">
367
- <div>
368
- <h2 id="addTitle" class="panel-title">New workspace</h2>
369
- <p class="panel-subtitle">Connect a working directory and a board to get started.</p>
370
- </div>
371
- </div>
372
- <form id="workspaceForm">
373
- <label>Canvas file
374
- <input id="boardInput" name="board" placeholder="/tmp/pcws-xxxx.html" spellcheck="false">
375
- </label>
376
- <label>Directory
377
- <input id="dirInput" name="dir" placeholder="./" spellcheck="false">
378
- </label>
379
- <label>Name <span>optional</span>
380
- <input id="nameInput" name="name" autocomplete="off" placeholder="workspace-1" spellcheck="false">
381
- </label>
382
- <div class="form-actions">
383
- <button id="addButton" class="primary" type="submit">Create workspace</button>
384
- </div>
385
- </form>
386
- </section>
387
-
388
- </div>
389
- </main>
390
-
391
- <script>
392
- const rows = document.getElementById("workspaceRows");
393
- const form = document.getElementById("workspaceForm");
394
- const statusEl = document.getElementById("status");
395
- const nameInput = document.getElementById("nameInput");
396
- const dirInput = document.getElementById("dirInput");
397
- const boardInput = document.getElementById("boardInput");
398
- const addButton = document.getElementById("addButton");
399
- const refreshButton = document.getElementById("refreshButton");
400
- const workspaceCount = document.getElementById("workspaceCount");
401
- const sessionCount = document.getElementById("sessionCount");
402
- const activeCount = document.getElementById("activeCount");
403
-
404
- function api(path) {
405
- return new URL(path, window.location.href).toString();
406
- }
407
-
408
- function setStatus(text, isError = false) {
409
- statusEl.textContent = text || "";
410
- statusEl.classList.toggle("error", Boolean(isError));
411
- }
412
-
413
- function textNode(tag, className, text) {
414
- const node = document.createElement(tag);
415
- node.className = className;
416
- node.textContent = text || "";
417
- return node;
418
- }
419
-
420
- function pathLine(label, value) {
421
- const row = document.createElement("div");
422
- row.className = "path-line";
423
- row.appendChild(textNode("span", "path-label", label));
424
- const path = textNode("span", "path-value", value || "");
425
- path.title = value || "";
426
- row.appendChild(path);
427
- return row;
428
- }
429
-
430
- function renderSummary(workspaces) {
431
- const sessions = [];
432
- workspaces.forEach((workspace) => {
433
- (workspace.sessions || []).forEach((session) => sessions.push(session));
434
- });
435
- workspaceCount.textContent = String(workspaces.length);
436
- sessionCount.textContent = String(sessions.length);
437
- activeCount.textContent = String(sessions.filter((session) => session.running).length);
438
- }
439
-
440
- function renderEmpty() {
441
- const empty = document.createElement("div");
442
- empty.className = "empty";
443
- empty.appendChild(textNode("strong", "", "No workspaces yet"));
444
- empty.appendChild(textNode("span", "", "Add one to open a board and start a pycodex session."));
445
- rows.appendChild(empty);
446
- }
447
-
448
- function renderWorkspace(workspace) {
449
- const row = document.createElement("article");
450
- row.className = "workspace-row";
451
-
452
- const name = document.createElement("div");
453
- name.className = "workspace-name";
454
- name.appendChild(textNode("strong", "", workspace.id || ""));
455
- const sessions = Array.isArray(workspace.sessions) ? workspace.sessions : [];
456
- name.appendChild(textNode("span", "session-count", `${sessions.length} session${sessions.length === 1 ? "" : "s"}`));
457
-
458
- const paths = document.createElement("div");
459
- paths.className = "paths";
460
- paths.appendChild(pathLine("Directory", workspace.work_dir || ""));
461
- paths.appendChild(pathLine("Canvas", workspace.board_path || ""));
462
-
463
- const actions = document.createElement("div");
464
- actions.className = "actions";
465
- const open = document.createElement("a");
466
- open.className = "button";
467
- open.href = `w/${encodeURIComponent(workspace.id || "")}/`;
468
- open.textContent = "Open";
469
- const remove = document.createElement("button");
470
- remove.className = "danger";
471
- remove.type = "button";
472
- remove.textContent = "Delete";
473
- remove.addEventListener("click", () => deleteWorkspace(workspace.id || ""));
474
- actions.appendChild(open);
475
- actions.appendChild(remove);
476
-
477
- row.appendChild(name);
478
- row.appendChild(paths);
479
- row.appendChild(actions);
480
- rows.appendChild(row);
481
- }
482
-
483
- function render(workspaces) {
484
- rows.textContent = "";
485
- renderSummary(workspaces);
486
- if (!workspaces.length) {
487
- renderEmpty();
488
- return;
489
- }
490
- workspaces.forEach(renderWorkspace);
491
- }
492
-
493
- async function refresh() {
494
- setStatus("Refreshing...");
495
- const response = await fetch(api("api/workspaces"));
496
- const payload = await response.json();
497
- if (!response.ok) {
498
- throw new Error(payload.error || `HTTP ${response.status}`);
499
- }
500
- render(Array.isArray(payload.workspaces) ? payload.workspaces : []);
501
- setStatus("");
502
- }
503
-
504
- async function addWorkspace(event) {
505
- event.preventDefault();
506
- setStatus("");
507
- addButton.disabled = true;
508
- const response = await fetch(api("api/workspaces"), {
509
- method: "POST",
510
- headers: {"Content-Type": "application/json"},
511
- body: JSON.stringify({
512
- name: nameInput.value.trim(),
513
- dir: dirInput.value.trim() || "./",
514
- board: boardInput.value.trim() || null,
515
- }),
516
- });
517
- const payload = await response.json();
518
- addButton.disabled = false;
519
- if (!response.ok || !payload.ok) {
520
- setStatus(payload.error || `HTTP ${response.status}`, true);
521
- return;
522
- }
523
- nameInput.value = "";
524
- dirInput.value = "";
525
- boardInput.value = "";
526
- render(payload.workspaces || []);
527
- setStatus(`Added ${(payload.workspace && payload.workspace.id) || "workspace"}.`);
528
- }
529
-
530
- async function deleteWorkspace(name) {
531
- if (!name) return;
532
- setStatus("");
533
- const response = await fetch(api(`api/workspaces/${encodeURIComponent(name)}`), {
534
- method: "DELETE",
535
- });
536
- const payload = await response.json();
537
- if (!response.ok || !payload.ok) {
538
- setStatus(payload.error || `HTTP ${response.status}`, true);
539
- return;
540
- }
541
- render(payload.workspaces || []);
542
- setStatus(`Deleted ${name}.`);
543
- }
544
-
545
- form.addEventListener("submit", addWorkspace);
546
- refreshButton.addEventListener("click", () => refresh().catch((error) => setStatus(error.message, true)));
547
- refresh().catch((error) => setStatus(error.message, true));
548
- </script>
549
- </body>
550
- </html>