@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.
- package/.github/extensions/web-relay/skills/reasoning-stream.mjs +3 -1
- package/CHANGELOG.md +284 -0
- package/README.md +12 -11
- package/package.json +5 -1
- package/server/claude-worker/claude-remote-relay-tool.mjs +15 -0
- package/server/claude-worker/claude-sdk-adapter.mjs +8 -4
- package/server/claude-worker/claude-session-process.mjs +4 -0
- package/server/claude-worker/claude-turn-publisher.mjs +25 -11
- package/server/claude-worker/sdk-message-normalizer.mjs +29 -1
- package/server/copilot-worker/copilot-prompt-context.mjs +2 -1
- package/server/copilot-worker/copilot-sdk-session-process.mjs +11 -9
- package/server/cursor-worker/cursor-turn-runner.mjs +3 -1
- package/server/cursor-worker/sdk-message-normalizer.mjs +13 -0
- package/server/db-schema.mjs +19 -0
- package/server/grok-worker/grok-turn-runner.mjs +3 -1
- package/server/grok-worker/sdk-message-normalizer.mjs +3 -0
- package/server/migrations/0039-media-index.mjs +167 -0
- package/server/migrations/0040-image-session-defaults.mjs +93 -0
- package/server/public/app/api-client.js +58 -0
- package/server/public/app/ask-user-view.js +2 -1
- package/server/public/app/attachments-view.js +66 -9
- package/server/public/app/bootstrap.js +85 -25
- package/server/public/app/chat-actions-menu-state.mjs +1 -0
- package/server/public/app/context-media-tab.js +143 -0
- package/server/public/app/context-usage-view.mjs +2 -2
- package/server/public/app/conversation-view.js +90 -5
- package/server/public/app/image-composer-ui.js +518 -0
- package/server/public/app/image-turn-placeholder.mjs +233 -0
- package/server/public/app/journal-view.js +12 -2
- package/server/public/app/live-activity-cap.mjs +3 -3
- package/server/public/app/markdown-math.mjs +471 -0
- package/server/public/app/media-gallery-model.mjs +202 -0
- package/server/public/app/media-gallery-ui.js +524 -0
- package/server/public/app/media-view.js +79 -0
- package/server/public/app/openai-image-composer.mjs +492 -0
- package/server/public/app/openai-image-cost-ui.js +426 -0
- package/server/public/app/openai-image-spend.mjs +443 -0
- package/server/public/app/plan-usage-view.mjs +38 -6
- package/server/public/app/pwa-install.js +25 -4
- package/server/public/app/relay-error-ctas.mjs +5 -0
- package/server/public/app/relay-prompt-sanitizer.mjs +3 -0
- package/server/public/app/relay-restart-ui.js +2 -2
- package/server/public/app/remote-relays-settings.js +33 -6
- package/server/public/app/router.js +49 -237
- package/server/public/app/session-history-ui.js +2 -0
- package/server/public/app/settings-modal.js +4 -0
- package/server/public/app/settings-tabs.js +10 -1
- package/server/public/app/socket-handlers.js +26 -0
- package/server/public/app/store.js +2 -0
- package/server/public/app/stream-view-preference.mjs +1 -1
- package/server/public/app/tab-strip-fade.mjs +1 -1
- package/server/public/app/thoughts-preference.mjs +1 -1
- package/server/public/app/tmux-inspector-view.js +29 -16
- package/server/public/app/viewer-gallery.mjs +23 -1
- package/server/public/index.html +517 -123
- package/server/relay-tools.md +1 -0
- package/server/repositories/image-conversation-repository.mjs +69 -0
- package/server/repositories/media-repository.mjs +305 -0
- package/server/routes/media-routes.mjs +361 -0
- package/server/routes/messages-routes.mjs +1410 -360
- package/server/routes/relay-board-routes.mjs +46 -1
- package/server/routes/remote-relay-routes.mjs +1 -1
- package/server/routes/sessions-routes.mjs +361 -14
- package/server/server-runtime.mjs +21 -0
- package/server/services/database-file.mjs +6 -4
- package/server/services/generated-image-cleanup-service.mjs +2 -1
- package/server/services/generated-image-files.mjs +110 -0
- package/server/services/image-input-paths.mjs +389 -0
- package/server/services/image-operation-service.mjs +6 -2
- package/server/services/media-extract.mjs +653 -0
- package/server/services/media-index-service.mjs +221 -0
- package/server/services/openai-image-preview-turns.mjs +330 -0
- package/server/services/openai-image-spend-service.mjs +516 -0
- package/server/services/openai-image-stream.mjs +170 -0
- package/server/services/plan-usage-openai-image.mjs +148 -0
- package/server/services/relay-prompt-sanitizer.mjs +7 -0
- package/server/services/relay-restart-queue.mjs +1 -1
- package/server/services/remote-relay-caller-context.mjs +6 -0
- package/server/services/remote-relay-dispatcher.mjs +409 -23
- package/server/services/remote-relay-registry-service.mjs +10 -2
- package/server/services/session-worker-stop-service.mjs +50 -18
- package/server/services/token-usage-service.mjs +15 -0
- package/shared/image-header.mjs +90 -0
- package/shared/math-instructions.mjs +30 -0
- package/shared/openai-image-options.mjs +299 -0
- package/shared/openai-image-presets.mjs +175 -0
- package/shared/openai-image-previews.mjs +40 -0
- package/shared/openai-image-prices.mjs +291 -0
- package/shared/openai-image-spend.mjs +90 -0
- package/shared/remote-relay-contract.mjs +126 -4
- package/shared/worker-runtime/direct-openai-image.mjs +96 -50
- package/server/public/app/math-dollars.mjs +0 -89
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OpenAI images on the Check Usage modal (provider `openai`, the booking's
|
|
5
|
+
* provider, so the Tokens sections of plan-usage-tokens.mjs land on it).
|
|
6
|
+
*
|
|
7
|
+
* The figures are the spend state of shared/openai-image-spend.mjs: today
|
|
8
|
+
* and this month in the relay's local days, booked by OAR from its price
|
|
9
|
+
* table, estimates and the stand-in price of an unknown model included (they
|
|
10
|
+
* count toward the cap on purpose). The card shows the daily cap as a meter,
|
|
11
|
+
* the warning share, the owner's "Allow for today", and how much of today is
|
|
12
|
+
* estimated. The raw state rides along as `imageSpend` for the browser's cap
|
|
13
|
+
* bar.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
SOURCE_WORKER,
|
|
18
|
+
STATUS_OK,
|
|
19
|
+
STATUS_UNAVAILABLE,
|
|
20
|
+
buildMeter,
|
|
21
|
+
buildProviderCard,
|
|
22
|
+
roundCurrency,
|
|
23
|
+
toFiniteNumber,
|
|
24
|
+
} from './plan-usage-contract.mjs';
|
|
25
|
+
import {
|
|
26
|
+
OPENAI_IMAGE_BOOKING_PROVIDER,
|
|
27
|
+
OPENAI_IMAGE_CAP_WARN_RATIO,
|
|
28
|
+
formatOpenAIImageUsd,
|
|
29
|
+
} from '../../shared/openai-image-spend.mjs';
|
|
30
|
+
|
|
31
|
+
export const OPENAI_IMAGE_CARD_LABEL = 'OpenAI images';
|
|
32
|
+
|
|
33
|
+
const OPENAI_USAGE_URL = 'https://platform.openai.com/usage';
|
|
34
|
+
|
|
35
|
+
const usd = (value) => Math.max(0, toFiniteNumber(value) ?? 0);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The card, or null when there is nothing to show: OpenAI is off and nothing
|
|
39
|
+
* was booked this month. `state` is the spend state; `error` replaces it
|
|
40
|
+
* when it could not be read.
|
|
41
|
+
*/
|
|
42
|
+
export function buildOpenAIImagePlanCard({ state = null, error = null, configured = false } = {}) {
|
|
43
|
+
const links = [{ label: 'OpenAI usage', url: OPENAI_USAGE_URL }];
|
|
44
|
+
if (!state) {
|
|
45
|
+
if (!configured) return null;
|
|
46
|
+
return buildProviderCard({
|
|
47
|
+
provider: OPENAI_IMAGE_BOOKING_PROVIDER,
|
|
48
|
+
label: OPENAI_IMAGE_CARD_LABEL,
|
|
49
|
+
status: STATUS_UNAVAILABLE,
|
|
50
|
+
message: `This relay's OpenAI image spend could not be read${error ? `: ${error}` : ''}.`,
|
|
51
|
+
source: SOURCE_WORKER,
|
|
52
|
+
links,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const spent = usd(state.spentUsd);
|
|
56
|
+
const reserved = usd(state.reservedUsd);
|
|
57
|
+
const month = usd(state.monthUsd);
|
|
58
|
+
if (!configured && month <= 0 && spent <= 0 && reserved <= 0) return null;
|
|
59
|
+
|
|
60
|
+
const capUsd = toFiniteNumber(state.capUsd);
|
|
61
|
+
const hasCap = capUsd !== null && capUsd > 0;
|
|
62
|
+
const used = roundCurrency(spent + reserved);
|
|
63
|
+
const allowed = state.allowedToday === true;
|
|
64
|
+
const percent = hasCap ? Math.round((used / capUsd) * 100) : null;
|
|
65
|
+
|
|
66
|
+
const messages = [];
|
|
67
|
+
if (hasCap && state.reached === true) {
|
|
68
|
+
messages.push(allowed
|
|
69
|
+
? `Today's image spend has reached the daily cap of ${formatOpenAIImageUsd(capUsd)}; image turns run anyway, because the owner allowed them for today.`
|
|
70
|
+
: `Today's image spend has reached the daily cap of ${formatOpenAIImageUsd(capUsd)}: new image turns are refused until tomorrow (relay time), or until the owner allows more for today (Settings → Providers → OpenAI).`);
|
|
71
|
+
} else if (hasCap && state.warning === true) {
|
|
72
|
+
messages.push(`Today's image spend is at ${percent} % of the daily cap of ${formatOpenAIImageUsd(capUsd)}.`);
|
|
73
|
+
}
|
|
74
|
+
if (!hasCap) messages.push('No daily cap is set for OpenAI images on this relay.');
|
|
75
|
+
|
|
76
|
+
const meters = [];
|
|
77
|
+
if (hasCap) {
|
|
78
|
+
meters.push(buildMeter({
|
|
79
|
+
id: 'openai-images-daily-cap',
|
|
80
|
+
label: 'Daily cap',
|
|
81
|
+
unit: 'usd',
|
|
82
|
+
used,
|
|
83
|
+
allowance: capUsd,
|
|
84
|
+
remaining: roundCurrency(Math.max(0, capUsd - used)),
|
|
85
|
+
estimated: true,
|
|
86
|
+
severity: state.reached === true ? 'reached' : (state.warning === true ? 'warning' : null),
|
|
87
|
+
note: `This relay's image spend today (${state.day || 'relay time'}), images in progress included; a warning from ${Math.round(OPENAI_IMAGE_CAP_WARN_RATIO * 100)} %, and at the cap new image turns are refused${allowed ? ' (allowed for today by the owner)' : ''}. It starts over at midnight, relay time.`,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const estimated = usd(state.estimatedUsd);
|
|
92
|
+
const unknown = usd(state.unknownPriceUsd);
|
|
93
|
+
const notes = [
|
|
94
|
+
{
|
|
95
|
+
id: 'openai-images-today',
|
|
96
|
+
label: 'Today',
|
|
97
|
+
value: formatOpenAIImageUsd(used),
|
|
98
|
+
hint: [
|
|
99
|
+
reserved > 0 ? `${formatOpenAIImageUsd(reserved)} in progress` : '',
|
|
100
|
+
estimated > 0 ? `${formatOpenAIImageUsd(estimated)} estimated` : '',
|
|
101
|
+
unknown > 0 ? `${formatOpenAIImageUsd(unknown)} at an unknown model's stand-in price` : '',
|
|
102
|
+
].filter(Boolean).join(' · ') || null,
|
|
103
|
+
text: `OpenAI image spend today: ${formatOpenAIImageUsd(used)}${reserved > 0 ? `, of it ${formatOpenAIImageUsd(reserved)} in progress` : ''}.`,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'openai-images-month',
|
|
107
|
+
label: 'This month',
|
|
108
|
+
value: formatOpenAIImageUsd(month),
|
|
109
|
+
text: `OpenAI image spend this month: ${formatOpenAIImageUsd(month)}.`,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'openai-images-override',
|
|
113
|
+
label: 'Allow for today',
|
|
114
|
+
value: allowed ? 'on' : 'off',
|
|
115
|
+
hint: allowed && state.allowedAt ? `since ${state.allowedAt}` : null,
|
|
116
|
+
text: allowed
|
|
117
|
+
? 'The owner allowed image turns past the cap for today; it ends at midnight, relay time.'
|
|
118
|
+
: 'Past the cap, image turns are refused unless the owner allows them for today.',
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
const conversation = state.conversation && typeof state.conversation === 'object' ? state.conversation : null;
|
|
122
|
+
if (conversation) {
|
|
123
|
+
const images = Math.max(0, Math.trunc(toFiniteNumber(conversation.images) ?? 0));
|
|
124
|
+
notes.push({
|
|
125
|
+
id: 'openai-images-this-chat',
|
|
126
|
+
label: 'This chat',
|
|
127
|
+
value: formatOpenAIImageUsd(usd(conversation.costUsd)),
|
|
128
|
+
hint: `${images} ${images === 1 ? 'image' : 'images'}`,
|
|
129
|
+
text: `OpenAI images in this chat: ${formatOpenAIImageUsd(usd(conversation.costUsd))} (${images} ${images === 1 ? 'image' : 'images'}).`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
notes.push({
|
|
133
|
+
id: 'openai-images-estimate',
|
|
134
|
+
text: 'Priced by OAR from its own table of OpenAI image prices, for the image turns of this relay; a try that may have been billed without a usage report counts at its estimate. OpenAI\'s usage page has every request on the key.',
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const card = buildProviderCard({
|
|
138
|
+
provider: OPENAI_IMAGE_BOOKING_PROVIDER,
|
|
139
|
+
label: OPENAI_IMAGE_CARD_LABEL,
|
|
140
|
+
status: STATUS_OK,
|
|
141
|
+
message: messages.join(' ') || null,
|
|
142
|
+
source: SOURCE_WORKER,
|
|
143
|
+
meters: meters.filter(Boolean),
|
|
144
|
+
notes,
|
|
145
|
+
links,
|
|
146
|
+
});
|
|
147
|
+
return card ? { ...card, imageSpend: state } : null;
|
|
148
|
+
}
|
|
@@ -4,6 +4,7 @@ import fs from 'fs';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
|
|
7
|
+
import { renderMathInstructionBlock } from '../../shared/math-instructions.mjs';
|
|
7
8
|
import { renderMediaEmbedInstructionBlock } from '../../shared/media-embed-instructions.mjs';
|
|
8
9
|
|
|
9
10
|
const DEFAULT_RELAY_TOOL_GUIDANCE = [
|
|
@@ -148,6 +149,11 @@ const MEDIA_EMBED_BLOCK_PATTERN = new RegExp(
|
|
|
148
149
|
whitespaceFlexiblePattern(renderMediaEmbedInstructionBlock()),
|
|
149
150
|
'gi',
|
|
150
151
|
);
|
|
152
|
+
// The formula guidance (shared/math-instructions.mjs) rides right behind it.
|
|
153
|
+
const MATH_BLOCK_PATTERN = new RegExp(
|
|
154
|
+
whitespaceFlexiblePattern(renderMathInstructionBlock()),
|
|
155
|
+
'gi',
|
|
156
|
+
);
|
|
151
157
|
|
|
152
158
|
// A steered message's hidden note (shared/steer-note.mjs) leads the prompt the
|
|
153
159
|
// runtime stored, right after any mode marker. Matched by its stable opening
|
|
@@ -167,6 +173,7 @@ function stripLeadingRelayNotes(text) {
|
|
|
167
173
|
export function stripRelayPromptContext(text, relayMode = '', attachments = []) {
|
|
168
174
|
const value = stripAttachmentPromptArtifacts(text)
|
|
169
175
|
.replace(MEDIA_EMBED_BLOCK_PATTERN, '')
|
|
176
|
+
.replace(MATH_BLOCK_PATTERN, '')
|
|
170
177
|
.trim();
|
|
171
178
|
if (!value) return '';
|
|
172
179
|
const patterns = buildPromptPrefixPatterns(relayMode);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* The request waits until no turn is processing (relay-shutdown-gate.mjs);
|
|
9
9
|
* while it waits, no pending row is handed to a worker. A turn that runs for
|
|
10
10
|
* hours would hold every session's new messages for hours, so the wait has a
|
|
11
|
-
* maximum (Settings ›
|
|
11
|
+
* maximum (Settings › Relays). Past it the relay does not force anything:
|
|
12
12
|
* the request stays queued and turns into a question — restart now, keep
|
|
13
13
|
* waiting another period, or cancel — that the banner shows and a push
|
|
14
14
|
* notification announces once. Only the user, or an API caller that says
|
|
@@ -153,6 +153,12 @@ function describeSettingsChange(args) {
|
|
|
153
153
|
if (toText(args.thinking_display)) parts.push(`Thinking text: ${toText(args.thinking_display) === 'omitted' ? 'hidden' : 'visible'}`);
|
|
154
154
|
if (toText(args.cwd)) parts.push(`Folder: ${excerpt(args.cwd)} (its CLI restarts there)`);
|
|
155
155
|
if (typeof args.planOnly === 'boolean') parts.push(`planOnly: ${args.planOnly}`);
|
|
156
|
+
if (args.image && typeof args.image === 'object' && !Array.isArray(args.image)) {
|
|
157
|
+
const options = Object.entries(args.image)
|
|
158
|
+
.filter(([, value]) => toText(value))
|
|
159
|
+
.map(([name, value]) => `${name.replace(/_/g, ' ')} ${excerpt(value)}`);
|
|
160
|
+
if (options.length) parts.push(`Image defaults: ${options.join(', ')}`);
|
|
161
|
+
}
|
|
156
162
|
return parts.join(' · ');
|
|
157
163
|
}
|
|
158
164
|
|