@oar-sh/oar 0.9.24 → 0.9.26

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 (92) hide show
  1. package/.github/extensions/web-relay/skills/reasoning-stream.mjs +3 -1
  2. package/CHANGELOG.md +284 -0
  3. package/README.md +12 -11
  4. package/package.json +5 -1
  5. package/server/claude-worker/claude-remote-relay-tool.mjs +15 -0
  6. package/server/claude-worker/claude-sdk-adapter.mjs +8 -4
  7. package/server/claude-worker/claude-session-process.mjs +4 -0
  8. package/server/claude-worker/claude-turn-publisher.mjs +25 -11
  9. package/server/claude-worker/sdk-message-normalizer.mjs +29 -1
  10. package/server/copilot-worker/copilot-prompt-context.mjs +2 -1
  11. package/server/copilot-worker/copilot-sdk-session-process.mjs +11 -9
  12. package/server/cursor-worker/cursor-turn-runner.mjs +3 -1
  13. package/server/cursor-worker/sdk-message-normalizer.mjs +13 -0
  14. package/server/db-schema.mjs +19 -0
  15. package/server/grok-worker/grok-turn-runner.mjs +3 -1
  16. package/server/grok-worker/sdk-message-normalizer.mjs +3 -0
  17. package/server/migrations/0039-media-index.mjs +167 -0
  18. package/server/migrations/0040-image-session-defaults.mjs +93 -0
  19. package/server/public/app/api-client.js +58 -0
  20. package/server/public/app/ask-user-view.js +2 -1
  21. package/server/public/app/attachments-view.js +66 -9
  22. package/server/public/app/bootstrap.js +85 -25
  23. package/server/public/app/chat-actions-menu-state.mjs +1 -0
  24. package/server/public/app/context-media-tab.js +143 -0
  25. package/server/public/app/context-usage-view.mjs +2 -2
  26. package/server/public/app/conversation-view.js +90 -5
  27. package/server/public/app/image-composer-ui.js +518 -0
  28. package/server/public/app/image-turn-placeholder.mjs +233 -0
  29. package/server/public/app/journal-view.js +12 -2
  30. package/server/public/app/live-activity-cap.mjs +3 -3
  31. package/server/public/app/markdown-math.mjs +471 -0
  32. package/server/public/app/media-gallery-model.mjs +202 -0
  33. package/server/public/app/media-gallery-ui.js +524 -0
  34. package/server/public/app/media-view.js +79 -0
  35. package/server/public/app/openai-image-composer.mjs +492 -0
  36. package/server/public/app/openai-image-cost-ui.js +426 -0
  37. package/server/public/app/openai-image-spend.mjs +443 -0
  38. package/server/public/app/plan-usage-view.mjs +38 -6
  39. package/server/public/app/pwa-install.js +25 -4
  40. package/server/public/app/relay-error-ctas.mjs +5 -0
  41. package/server/public/app/relay-prompt-sanitizer.mjs +3 -0
  42. package/server/public/app/relay-restart-ui.js +2 -2
  43. package/server/public/app/remote-relays-settings.js +33 -6
  44. package/server/public/app/router.js +49 -237
  45. package/server/public/app/session-history-ui.js +2 -0
  46. package/server/public/app/settings-modal.js +4 -0
  47. package/server/public/app/settings-tabs.js +10 -1
  48. package/server/public/app/socket-handlers.js +26 -0
  49. package/server/public/app/store.js +2 -0
  50. package/server/public/app/stream-view-preference.mjs +1 -1
  51. package/server/public/app/tab-strip-fade.mjs +1 -1
  52. package/server/public/app/thoughts-preference.mjs +1 -1
  53. package/server/public/app/tmux-inspector-view.js +29 -16
  54. package/server/public/app/viewer-gallery.mjs +23 -1
  55. package/server/public/index.html +517 -123
  56. package/server/relay-tools.md +1 -0
  57. package/server/repositories/image-conversation-repository.mjs +69 -0
  58. package/server/repositories/media-repository.mjs +305 -0
  59. package/server/routes/media-routes.mjs +361 -0
  60. package/server/routes/messages-routes.mjs +1410 -360
  61. package/server/routes/relay-board-routes.mjs +46 -1
  62. package/server/routes/remote-relay-routes.mjs +1 -1
  63. package/server/routes/sessions-routes.mjs +361 -14
  64. package/server/server-runtime.mjs +21 -0
  65. package/server/services/database-file.mjs +6 -4
  66. package/server/services/generated-image-cleanup-service.mjs +2 -1
  67. package/server/services/generated-image-files.mjs +110 -0
  68. package/server/services/image-input-paths.mjs +389 -0
  69. package/server/services/image-operation-service.mjs +6 -2
  70. package/server/services/media-extract.mjs +653 -0
  71. package/server/services/media-index-service.mjs +221 -0
  72. package/server/services/openai-image-preview-turns.mjs +330 -0
  73. package/server/services/openai-image-spend-service.mjs +516 -0
  74. package/server/services/openai-image-stream.mjs +170 -0
  75. package/server/services/plan-usage-openai-image.mjs +148 -0
  76. package/server/services/relay-prompt-sanitizer.mjs +7 -0
  77. package/server/services/relay-restart-queue.mjs +1 -1
  78. package/server/services/remote-relay-caller-context.mjs +6 -0
  79. package/server/services/remote-relay-dispatcher.mjs +409 -23
  80. package/server/services/remote-relay-registry-service.mjs +10 -2
  81. package/server/services/session-worker-stop-service.mjs +50 -18
  82. package/server/services/token-usage-service.mjs +15 -0
  83. package/shared/image-header.mjs +90 -0
  84. package/shared/math-instructions.mjs +30 -0
  85. package/shared/openai-image-options.mjs +299 -0
  86. package/shared/openai-image-presets.mjs +175 -0
  87. package/shared/openai-image-previews.mjs +40 -0
  88. package/shared/openai-image-prices.mjs +291 -0
  89. package/shared/openai-image-spend.mjs +90 -0
  90. package/shared/remote-relay-contract.mjs +126 -4
  91. package/shared/worker-runtime/direct-openai-image.mjs +96 -50
  92. package/server/public/app/math-dollars.mjs +0 -89
@@ -14,17 +14,22 @@ let inspectorState = {
14
14
  listenersBound: false,
15
15
  };
16
16
 
17
- const XTERM_STYLE_CANDIDATES = [
18
- 'https://cdn.jsdelivr.net/npm/xterm@5.5.0/css/xterm.min.css',
19
- 'https://unpkg.com/xterm@5.5.0/css/xterm.min.css',
17
+ // The files index.html loads, and unpkg as the fallback: one package version
18
+ // and one hash per file, so either CDN must serve the same bytes.
19
+ const XTERM_STYLE_INTEGRITY = 'sha384-8Xk9wy/gzEDUKrXtrmCFa2bBuK3BpjpDuL/p0SeKQX19Khl/M+lHOgD/CyYf7efP';
20
+ const XTERM_SCRIPT_INTEGRITY = 'sha384-M169f14mRZOXm3hD/v2Ti0ThIT/RnAQagXA9nlE15yHAtrW19gdePJh/HaTzUOe/';
21
+ const XTERM_FIT_SCRIPT_INTEGRITY = 'sha384-iF+jqbuti4XlB64clWgFWYEscb+UnSRv3VgVikGYZu+otNFnSHr7y7NcKfBnGizn';
22
+ export const XTERM_STYLE_CANDIDATES = [
23
+ { url: 'https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.css', integrity: XTERM_STYLE_INTEGRITY },
24
+ { url: 'https://unpkg.com/@xterm/xterm@5.5.0/css/xterm.css', integrity: XTERM_STYLE_INTEGRITY },
20
25
  ];
21
- const XTERM_SCRIPT_CANDIDATES = [
22
- 'https://cdn.jsdelivr.net/npm/xterm@5.5.0/lib/xterm.min.js',
23
- 'https://unpkg.com/xterm@5.5.0/lib/xterm.min.js',
26
+ export const XTERM_SCRIPT_CANDIDATES = [
27
+ { url: 'https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.js', integrity: XTERM_SCRIPT_INTEGRITY },
28
+ { url: 'https://unpkg.com/@xterm/xterm@5.5.0/lib/xterm.js', integrity: XTERM_SCRIPT_INTEGRITY },
24
29
  ];
25
- const XTERM_FIT_SCRIPT_CANDIDATES = [
26
- 'https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.10.0/lib/xterm-addon-fit.min.js',
27
- 'https://unpkg.com/xterm-addon-fit@0.10.0/lib/xterm-addon-fit.min.js',
30
+ export const XTERM_FIT_SCRIPT_CANDIDATES = [
31
+ { url: 'https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js', integrity: XTERM_FIT_SCRIPT_INTEGRITY },
32
+ { url: 'https://unpkg.com/@xterm/addon-fit@0.10.0/lib/addon-fit.js', integrity: XTERM_FIT_SCRIPT_INTEGRITY },
28
33
  ];
29
34
 
30
35
  let xtermLoadPromise = null;
@@ -64,7 +69,7 @@ function stripAnsiControlCodes(input = '') {
64
69
  .replace(/\r(?!\n)/g, '');
65
70
  }
66
71
 
67
- function loadScript(src) {
72
+ function loadScript(src, integrity = '') {
68
73
  return new Promise((resolve, reject) => {
69
74
  const timeoutMs = 3500;
70
75
  let settled = false;
@@ -101,6 +106,10 @@ function loadScript(src) {
101
106
  }
102
107
  const script = document.createElement('script');
103
108
  script.src = src;
109
+ if (integrity) {
110
+ script.integrity = integrity;
111
+ script.crossOrigin = 'anonymous';
112
+ }
104
113
  script.async = true;
105
114
  script.onload = () => {
106
115
  script.dataset.loaded = 'true';
@@ -114,9 +123,9 @@ function loadScript(src) {
114
123
  });
115
124
  }
116
125
 
117
- function ensureStylesheet(urlCandidates = []) {
118
- for (const href of urlCandidates) {
119
- const hrefText = String(href || '').trim();
126
+ function ensureStylesheet(candidates = []) {
127
+ for (const { url, integrity } of candidates) {
128
+ const hrefText = String(url || '').trim();
120
129
  if (!hrefText) continue;
121
130
  const already = Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
122
131
  .some((node) => String(node.getAttribute('href') || '').trim() === hrefText);
@@ -124,6 +133,10 @@ function ensureStylesheet(urlCandidates = []) {
124
133
  const link = document.createElement('link');
125
134
  link.rel = 'stylesheet';
126
135
  link.href = hrefText;
136
+ if (integrity) {
137
+ link.integrity = integrity;
138
+ link.crossOrigin = 'anonymous';
139
+ }
127
140
  document.head.appendChild(link);
128
141
  return;
129
142
  }
@@ -131,11 +144,11 @@ function ensureStylesheet(urlCandidates = []) {
131
144
 
132
145
  async function loadFirstSuccessful(candidates = []) {
133
146
  let lastError = null;
134
- for (const candidate of candidates) {
135
- const src = String(candidate || '').trim();
147
+ for (const { url, integrity } of candidates) {
148
+ const src = String(url || '').trim();
136
149
  if (!src) continue;
137
150
  try {
138
- await loadScript(src);
151
+ await loadScript(src, integrity);
139
152
  return true;
140
153
  } catch (error) {
141
154
  lastError = error;
@@ -32,7 +32,29 @@ export function galleryAt(gallery, index) {
32
32
 
33
33
  export function galleryPositionLabel(gallery) {
34
34
  if (!gallery || !Array.isArray(gallery.items) || !gallery.items.length) return '';
35
- return `${gallery.index + 1} / ${gallery.items.length}`;
35
+ return `${gallery.index + 1} / ${gallery.items.length}${galleryHasMore(gallery) ? '+' : ''}`;
36
+ }
37
+
38
+ // A paged gallery (the media gallery) knows only the pages loaded so far:
39
+ // `hasMore` says more items follow and `loadMore()` fetches the next page
40
+ // as `{ items, hasMore }`. Stepping past the last loaded item loads it.
41
+
42
+ export function galleryHasMore(gallery) {
43
+ return Boolean(gallery && gallery.hasMore && typeof gallery.loadMore === 'function');
44
+ }
45
+
46
+ /** Whether a step of `delta` can land somewhere: a neighbour, or a page still to load. */
47
+ export function galleryCanStep(gallery, delta) {
48
+ if (galleryNeighborIndex(gallery, delta) !== -1) return true;
49
+ return Number(delta) > 0 && galleryHasMore(gallery) && Number(gallery.index) === gallery.items.length - 1;
50
+ }
51
+
52
+ /** The gallery with a loaded page appended (items it already has are not added twice). */
53
+ export function extendGallery(gallery, page) {
54
+ if (!gallery) return null;
55
+ const known = new Set(gallery.items.map((item) => item.key).filter(Boolean));
56
+ const added = (Array.isArray(page?.items) ? page.items : []).filter((item) => !item?.key || !known.has(item.key));
57
+ return { ...gallery, items: [...gallery.items, ...added], hasMore: Boolean(page?.hasMore) && added.length > 0 };
36
58
  }
37
59
 
38
60
  /** The items on either side of the open one, nearest first. */