@oar-sh/oar 0.9.24 → 0.9.25
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 +142 -0
- package/README.md +12 -11
- package/package.json +3 -1
- package/server/claude-worker/claude-remote-relay-tool.mjs +15 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +29 -1
- package/server/copilot-worker/copilot-sdk-session-process.mjs +11 -9
- package/server/cursor-worker/sdk-message-normalizer.mjs +13 -0
- package/server/db-schema.mjs +19 -0
- 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/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/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/relay-error-ctas.mjs +5 -0
- package/server/public/app/session-history-ui.js +2 -0
- package/server/public/app/settings-modal.js +4 -0
- package/server/public/app/socket-handlers.js +26 -0
- package/server/public/app/tab-strip-fade.mjs +1 -1
- package/server/public/app/viewer-gallery.mjs +23 -1
- package/server/public/index.html +383 -7
- 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/sessions-routes.mjs +361 -14
- package/server/server-runtime.mjs +19 -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/remote-relay-caller-context.mjs +6 -0
- package/server/services/remote-relay-dispatcher.mjs +386 -22
- package/server/services/remote-relay-registry-service.mjs +3 -1
- package/server/services/token-usage-service.mjs +15 -0
- package/shared/image-header.mjs +90 -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
|
@@ -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
|
+
}
|
|
@@ -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
|
|