opencode-reviewer 0.5.0 → 0.6.0
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/README.md +19 -9
- package/dist/tui.js +416 -192
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,6 +92,12 @@ The rating remains provisional until the full response is validated. A format re
|
|
|
92
92
|
clears the preview and restores the loading indicator. Auto-approval
|
|
93
93
|
starts only after the full response is validated and rendered.
|
|
94
94
|
|
|
95
|
+
Transient connection failures and HTTP 408/429/500/502/503/504 responses get up to
|
|
96
|
+
two internal retries within `timeoutMs`, honoring server cooldowns. A stream that
|
|
97
|
+
has already delivered assessment text is not restarted. This is separate from
|
|
98
|
+
`formatRetries`; no additional setting is needed. Retried requests can incur
|
|
99
|
+
additional provider charges, and unreported usage remains unknown.
|
|
100
|
+
|
|
95
101
|
During an auto-approval countdown, click the countdown to allow once immediately,
|
|
96
102
|
or Cancel to leave the request manual. Hiding or covering the panel also cancels
|
|
97
103
|
that request's countdown. Cancel before using native Allow always or rejection
|
|
@@ -110,6 +116,12 @@ costs use reported charges; other endpoints use available catalog estimates.
|
|
|
110
116
|
Received usage counts even if a review fails or is interrupted. Unreported charges
|
|
111
117
|
are missing from the totals.
|
|
112
118
|
|
|
119
|
+
Lifetime statistics also show **Safe** and **Unsafe** review counts. Each completed,
|
|
120
|
+
validated review counts once, even without usage data. Previews, retries and failed
|
|
121
|
+
or interrupted analyses do not count. Re-enabling a conversation can produce a new
|
|
122
|
+
review, which counts again. The totals persist across restarts; earlier ratings
|
|
123
|
+
were not recorded and cannot be reconstructed.
|
|
124
|
+
|
|
113
125
|
Reviews send the pending request, latest user prompt, project context, and relevant
|
|
114
126
|
file snapshots, diffs, or tool arguments to your endpoint before approval. Shell
|
|
115
127
|
file snapshots can follow symlinks outside the project. Missing evidence is noted
|
|
@@ -117,21 +129,19 @@ in the report; ratings are advice based on the supplied evidence.
|
|
|
117
129
|
|
|
118
130
|
### Desktop notifications
|
|
119
131
|
|
|
120
|
-
Transient banners have an **Opencode** heading and a small status icon: green
|
|
121
|
-
checkmark for approvals, orange exclamation mark for attention
|
|
132
|
+
Transient banners have an **Opencode (Session name)** heading and a small status icon: green
|
|
133
|
+
checkmark for approvals, orange exclamation mark for attention, red X
|
|
122
134
|
for errors, and a neutral code mark for completed responses. GNOME controls the
|
|
123
|
-
heading's font weight. The event
|
|
124
|
-
|
|
125
|
-
An explicit separator keeps them distinct on desktops that flatten banner text:
|
|
135
|
+
heading's font weight. The event message appears beneath it, even while the
|
|
136
|
+
terminal is focused:
|
|
126
137
|
|
|
127
138
|
- **Session needs attention:** questions and manual permissions. Reviewed requests
|
|
128
139
|
wait for a final validated assessment; failures, canceled automation, and a Safe
|
|
129
140
|
review blocked from starting its countdown also notify. Unreviewed requests
|
|
130
141
|
notify immediately, including when conversation review is disabled.
|
|
131
|
-
- **Reviewer
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
sends only the confirmed approval notification. Approval sounds are limited to
|
|
142
|
+
- **Reviewer approved a permission:** sent with the approval sound after confirmed
|
|
143
|
+
automatic approval, for both positive and zero delays. The countdown is silent.
|
|
144
|
+
Approval sounds are limited to
|
|
135
145
|
one every two seconds; every eligible banner is retained for delivery.
|
|
136
146
|
- **Session error:** an unrecovered session/provider failure, not a review failure.
|
|
137
147
|
- **Session ended:** a completed root-agent response, not a question/permission
|
package/dist/tui.js
CHANGED
|
@@ -550,10 +550,10 @@ var require_shell_quote = __commonJS({
|
|
|
550
550
|
// src/tui.tsx
|
|
551
551
|
init_define_REVIEW_PROMPTS();
|
|
552
552
|
init_define_REVIEW_SOUNDS();
|
|
553
|
-
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
554
|
-
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
555
553
|
import { use as _$use } from "@opentui/solid";
|
|
556
554
|
import { effect as _$effect } from "@opentui/solid";
|
|
555
|
+
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
556
|
+
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
557
557
|
import { insert as _$insert } from "@opentui/solid";
|
|
558
558
|
import { memo as _$memo2 } from "@opentui/solid";
|
|
559
559
|
import { createComponent as _$createComponent2 } from "@opentui/solid";
|
|
@@ -1128,6 +1128,79 @@ async function override(directory, name, signal) {
|
|
|
1128
1128
|
// src/usage.ts
|
|
1129
1129
|
init_define_REVIEW_PROMPTS();
|
|
1130
1130
|
init_define_REVIEW_SOUNDS();
|
|
1131
|
+
|
|
1132
|
+
// src/ui-text.ts
|
|
1133
|
+
init_define_REVIEW_PROMPTS();
|
|
1134
|
+
init_define_REVIEW_SOUNDS();
|
|
1135
|
+
var uiText = {
|
|
1136
|
+
review: {
|
|
1137
|
+
heading: "Permission analysis",
|
|
1138
|
+
safe: "\u2713 Safe",
|
|
1139
|
+
unsafe: "\u2717 Unsafe",
|
|
1140
|
+
unavailable: "! Analysis unavailable",
|
|
1141
|
+
evaluating: "Evaluating",
|
|
1142
|
+
retrying: "Retrying",
|
|
1143
|
+
staticIndicator: "[\u22EF]",
|
|
1144
|
+
failed: "Review failed",
|
|
1145
|
+
invalidConfiguration: "Invalid configuration"
|
|
1146
|
+
},
|
|
1147
|
+
autoApproval: {
|
|
1148
|
+
countdown: (seconds) => `Allowed in ${seconds}s`,
|
|
1149
|
+
checking: "Checking\u2026",
|
|
1150
|
+
cancel: "Cancel",
|
|
1151
|
+
allowing: "Allowing\u2026",
|
|
1152
|
+
cancelled: "Auto-approval canceled",
|
|
1153
|
+
unavailable: "! Auto-approval unavailable. Use native controls."
|
|
1154
|
+
},
|
|
1155
|
+
commands: {
|
|
1156
|
+
category: "Reviewer",
|
|
1157
|
+
enable: "Reviewer: Enable for conversation",
|
|
1158
|
+
disable: "Reviewer: Disable for conversation",
|
|
1159
|
+
lifetime: "Reviewer: Lifetime usage"
|
|
1160
|
+
},
|
|
1161
|
+
sessionMode: {
|
|
1162
|
+
saved: (enabled) => enabled ? "Reviewer enabled for this conversation." : "Reviewer disabled for this conversation.",
|
|
1163
|
+
saveFailed: (enabled) => enabled ? "Reviewer enabled locally, but saving failed. Resume may use the previous setting." : "Reviewer disabled locally, but saving failed. Resume may use the previous setting.",
|
|
1164
|
+
unavailable: "Reviewer setting unavailable. Session ancestry or saved mode could not be read."
|
|
1165
|
+
},
|
|
1166
|
+
usage: {
|
|
1167
|
+
tokens: (input, output) => `token: ${input} in ${output} out`,
|
|
1168
|
+
cost: (amount2) => `cost: $${amount2}`
|
|
1169
|
+
},
|
|
1170
|
+
lifetime: {
|
|
1171
|
+
title: "Reviewer lifetime usage",
|
|
1172
|
+
loading: "Loading recorded usage\u2026",
|
|
1173
|
+
unavailable: "Lifetime usage unavailable. Recorded totals have not been reset.",
|
|
1174
|
+
inlineUnavailable: "lifetime: usage unavailable",
|
|
1175
|
+
empty: "lifetime: no recorded usage",
|
|
1176
|
+
costUnavailable: "lifetime: cost unavailable",
|
|
1177
|
+
cost: (amount2, partial) => `lifetime: $${amount2}${partial ? " (partial pricing)" : ""}`,
|
|
1178
|
+
requests: (count3) => `${count3} requests with recorded usage`,
|
|
1179
|
+
tokens: (input, output, partial) => `token: ${input} in ${output} out${partial ? " (partial coverage)" : ""}`,
|
|
1180
|
+
tokensUnavailable: "tokens unavailable",
|
|
1181
|
+
tokenCoverage: (available, total) => `Token counts available: ${available}/${total} requests`,
|
|
1182
|
+
pricingCoverage: (available, total) => `Pricing available: ${available}/${total} requests`,
|
|
1183
|
+
since: (date) => `Recorded since: ${date}`,
|
|
1184
|
+
ratings: (safe, unsafe) => `Safe: ${safe} \xB7 Unsafe: ${unsafe}`,
|
|
1185
|
+
ratingsSince: (date) => `Ratings recorded since: ${date}`,
|
|
1186
|
+
ratingsExplanation: "Ratings count completed, validated reviews, including those without usage data. Previews, retries and failed or interrupted analyses do not count. A fresh review after re-enabling counts again. Earlier ratings were not recorded and cannot be recovered.",
|
|
1187
|
+
explanation: "Received usage only, including interrupted or unsuccessful reviews. Unreported charges remain unknown. Costs combine endpoint-reported amounts and catalog estimates; legacy history retains its original estimates. Earlier unrecorded usage cannot be recovered."
|
|
1188
|
+
},
|
|
1189
|
+
notifications: {
|
|
1190
|
+
application: "Opencode",
|
|
1191
|
+
heading: (session) => `Opencode (${session})`,
|
|
1192
|
+
openSession: "Open session",
|
|
1193
|
+
fallbackSession: "OpenCode session",
|
|
1194
|
+
attention: "Session needs attention",
|
|
1195
|
+
approved: "Reviewer approved a permission",
|
|
1196
|
+
error: "Session error",
|
|
1197
|
+
ended: "Session ended",
|
|
1198
|
+
// Arguments are escaped by the desktop adapter before formatting.
|
|
1199
|
+
body: (event) => event
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
// src/usage.ts
|
|
1131
1204
|
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1132
1205
|
var count = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
1133
1206
|
var rate = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
@@ -1217,8 +1290,8 @@ function sumUsage(previous, next) {
|
|
|
1217
1290
|
}
|
|
1218
1291
|
function usageText(usage) {
|
|
1219
1292
|
return [
|
|
1220
|
-
...count(usage.input) && count(usage.output) ? [
|
|
1221
|
-
...usage.cost === void 0 ? [] : [
|
|
1293
|
+
...count(usage.input) && count(usage.output) ? [uiText.usage.tokens(usage.input, usage.output)] : [],
|
|
1294
|
+
...usage.cost === void 0 ? [] : [uiText.usage.cost(usage.cost.toFixed(4))]
|
|
1222
1295
|
].join("\n");
|
|
1223
1296
|
}
|
|
1224
1297
|
|
|
@@ -1532,6 +1605,74 @@ function measured(trace, phase, run) {
|
|
|
1532
1605
|
return trace ? trace.measure(phase, run) : run();
|
|
1533
1606
|
}
|
|
1534
1607
|
|
|
1608
|
+
// src/transport-retry.ts
|
|
1609
|
+
init_define_REVIEW_PROMPTS();
|
|
1610
|
+
init_define_REVIEW_SOUNDS();
|
|
1611
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
1612
|
+
var statuses = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
1613
|
+
var networkCodes = /* @__PURE__ */ new Set([
|
|
1614
|
+
"EAI_AGAIN",
|
|
1615
|
+
"ECONNRESET",
|
|
1616
|
+
"ECONNREFUSED",
|
|
1617
|
+
"ECONNABORTED",
|
|
1618
|
+
"EPIPE",
|
|
1619
|
+
"ETIMEDOUT",
|
|
1620
|
+
"ENETUNREACH",
|
|
1621
|
+
"EHOSTUNREACH",
|
|
1622
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
1623
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
1624
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
1625
|
+
"UND_ERR_SOCKET"
|
|
1626
|
+
]);
|
|
1627
|
+
var TransientTransportError = class extends Error {
|
|
1628
|
+
constructor(message, notBefore = 0) {
|
|
1629
|
+
super(message);
|
|
1630
|
+
this.notBefore = notBefore;
|
|
1631
|
+
}
|
|
1632
|
+
notBefore;
|
|
1633
|
+
};
|
|
1634
|
+
function networkFailure(error, message) {
|
|
1635
|
+
for (let depth = 0; depth < 4 && error && typeof error === "object"; depth++) {
|
|
1636
|
+
const value = error;
|
|
1637
|
+
if (typeof value.code === "string") {
|
|
1638
|
+
return networkCodes.has(value.code) ? new TransientTransportError(message) : new Error(message);
|
|
1639
|
+
}
|
|
1640
|
+
error = value.cause;
|
|
1641
|
+
}
|
|
1642
|
+
return new Error(message);
|
|
1643
|
+
}
|
|
1644
|
+
function retryAfter(value, now = Date.now()) {
|
|
1645
|
+
if (value === null) return 0;
|
|
1646
|
+
value = value.trim();
|
|
1647
|
+
if (/^\d+$/.test(value)) return Number(value) * 1e3;
|
|
1648
|
+
if (!/^(?:[A-Za-z]{3}, \d{2} [A-Za-z]{3} \d{4} \d{2}:\d{2}:\d{2} GMT|[A-Za-z]+, \d{2}-[A-Za-z]{3}-\d{2} \d{2}:\d{2}:\d{2} GMT|[A-Za-z]{3} [A-Za-z]{3} [ \d]\d \d{2}:\d{2}:\d{2} \d{4})$/.test(value)) return 0;
|
|
1649
|
+
const date = Date.parse(value.endsWith("GMT") ? value : `${value} GMT`);
|
|
1650
|
+
return Number.isFinite(date) ? Math.max(0, date - now) : 0;
|
|
1651
|
+
}
|
|
1652
|
+
function httpFailure(response) {
|
|
1653
|
+
const message = `Reviewer HTTP ${response.status}`;
|
|
1654
|
+
return statuses.has(response.status) ? new TransientTransportError(message, performance.now() + retryAfter(response.headers.get("retry-after"))) : new Error(message);
|
|
1655
|
+
}
|
|
1656
|
+
var TransportRetries = class {
|
|
1657
|
+
used = 0;
|
|
1658
|
+
async wait(error, signal) {
|
|
1659
|
+
reviewStage(signal, "Reviewer response");
|
|
1660
|
+
if (!(error instanceof TransientTransportError) || this.used >= 2) return false;
|
|
1661
|
+
const base = 250 * 2 ** this.used;
|
|
1662
|
+
const delay = Math.max(base + Math.random() * base, error.notBefore - performance.now());
|
|
1663
|
+
if (delay + 250 >= remainingTime(signal)) return false;
|
|
1664
|
+
this.used++;
|
|
1665
|
+
try {
|
|
1666
|
+
await sleep(Math.ceil(delay), void 0, { signal });
|
|
1667
|
+
} catch {
|
|
1668
|
+
signal.throwIfAborted();
|
|
1669
|
+
throw error;
|
|
1670
|
+
}
|
|
1671
|
+
reviewStage(signal, "Reviewer response");
|
|
1672
|
+
return remainingTime(signal) > 250;
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
|
|
1535
1676
|
// src/reviewer.ts
|
|
1536
1677
|
function abortable(promise, signal, late) {
|
|
1537
1678
|
return new Promise((resolve, reject) => {
|
|
@@ -1576,9 +1717,9 @@ async function readBody(response, signal, consume) {
|
|
|
1576
1717
|
let part;
|
|
1577
1718
|
try {
|
|
1578
1719
|
part = await abortable(reader2.read(), signal);
|
|
1579
|
-
} catch {
|
|
1720
|
+
} catch (error) {
|
|
1580
1721
|
signal.throwIfAborted();
|
|
1581
|
-
throw
|
|
1722
|
+
throw networkFailure(error, "Reviewer response read failed");
|
|
1582
1723
|
}
|
|
1583
1724
|
signal.throwIfAborted();
|
|
1584
1725
|
if (part.done) break;
|
|
@@ -1677,6 +1818,15 @@ async function streamedAssessment(response, signal, accounting, progress, firstC
|
|
|
1677
1818
|
return assessment;
|
|
1678
1819
|
}
|
|
1679
1820
|
async function review(evidence, config, signal, fetcher = fetch, environment = process.env, prompts = BUILTIN_PROMPTS, pricing, onUsage, onProgress, onDiagnostics) {
|
|
1821
|
+
if (remainingTime(signal) === Infinity) {
|
|
1822
|
+
let worker;
|
|
1823
|
+
try {
|
|
1824
|
+
return await withDeadline(signal, config.timeoutMs, (bounded) => worker = review(evidence, config, bounded, fetcher, environment, prompts, pricing, onUsage, onProgress, onDiagnostics));
|
|
1825
|
+
} finally {
|
|
1826
|
+
await worker?.catch(() => {
|
|
1827
|
+
});
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1680
1830
|
const key = config.apiKey ?? (config.apiKeyEnv ? environment[config.apiKeyEnv]?.trim() : void 0);
|
|
1681
1831
|
if (config.apiKeyEnv && !key) throw new Error(`API key environment variable ${config.apiKeyEnv} is unset or empty`);
|
|
1682
1832
|
const prompt2 = prompts[evidence.kind];
|
|
@@ -1685,7 +1835,9 @@ async function review(evidence, config, signal, fetcher = fetch, environment = p
|
|
|
1685
1835
|
{ role: "user", content: JSON.stringify(evidence) }
|
|
1686
1836
|
];
|
|
1687
1837
|
let usage;
|
|
1688
|
-
|
|
1838
|
+
let corrections = 0;
|
|
1839
|
+
const retries = new TransportRetries();
|
|
1840
|
+
for (let attempt = 0; ; attempt++) {
|
|
1689
1841
|
signal.throwIfAborted();
|
|
1690
1842
|
const progress = (phase, preview) => {
|
|
1691
1843
|
if (signal.aborted) return;
|
|
@@ -1697,6 +1849,10 @@ async function review(evidence, config, signal, fetcher = fetch, environment = p
|
|
|
1697
1849
|
};
|
|
1698
1850
|
progress(attempt ? "retrying" : "evaluating");
|
|
1699
1851
|
const accounting = usageAttempt(config.baseURL, config.model, pricing, onUsage);
|
|
1852
|
+
const requestAbort = new AbortController();
|
|
1853
|
+
let contentStarted = false;
|
|
1854
|
+
let failed = false;
|
|
1855
|
+
let failure;
|
|
1700
1856
|
try {
|
|
1701
1857
|
reviewStage(signal, "Reviewer response");
|
|
1702
1858
|
let response;
|
|
@@ -1708,28 +1864,32 @@ async function review(evidence, config, signal, fetcher = fetch, environment = p
|
|
|
1708
1864
|
stream: config.stream,
|
|
1709
1865
|
...config.stream ? { stream_options: { include_usage: true } } : {}
|
|
1710
1866
|
});
|
|
1867
|
+
reviewStage(signal, "Reviewer response");
|
|
1711
1868
|
diagnose = onDiagnostics ? diagnosticAttempt(onDiagnostics, attempt) : void 0;
|
|
1712
1869
|
response = await abortable(fetcher(`${config.baseURL}/chat/completions`, {
|
|
1713
1870
|
method: "POST",
|
|
1714
1871
|
redirect: "error",
|
|
1715
|
-
signal,
|
|
1872
|
+
signal: AbortSignal.any([signal, requestAbort.signal]),
|
|
1716
1873
|
headers: { "Content-Type": "application/json", ...key ? { Authorization: `Bearer ${key}` } : {} },
|
|
1717
1874
|
body
|
|
1718
1875
|
}), signal, cancelBody);
|
|
1719
1876
|
diagnose?.("headers");
|
|
1720
|
-
} catch {
|
|
1877
|
+
} catch (error) {
|
|
1721
1878
|
signal.throwIfAborted();
|
|
1722
|
-
throw
|
|
1879
|
+
throw networkFailure(error, "Reviewer network request failed");
|
|
1723
1880
|
}
|
|
1724
1881
|
if (!response.ok) {
|
|
1725
1882
|
cancelBody(response);
|
|
1726
|
-
throw
|
|
1883
|
+
throw httpFailure(response);
|
|
1727
1884
|
}
|
|
1728
1885
|
let assessment;
|
|
1729
1886
|
if (config.stream) assessment = await streamedAssessment(response, signal, accounting, (preview) => {
|
|
1730
1887
|
if (typeof preview?.safe === "boolean") diagnose?.("first-rating");
|
|
1731
1888
|
progress("streaming", preview);
|
|
1732
|
-
},
|
|
1889
|
+
}, () => {
|
|
1890
|
+
contentStarted = true;
|
|
1891
|
+
diagnose?.("first-content");
|
|
1892
|
+
});
|
|
1733
1893
|
else {
|
|
1734
1894
|
let envelope;
|
|
1735
1895
|
try {
|
|
@@ -1747,14 +1907,17 @@ async function review(evidence, config, signal, fetcher = fetch, environment = p
|
|
|
1747
1907
|
if (!object(choice) || !object(message) || typeof message.content !== "string" || !textMetadata(message) || object(envelope) && envelope.error != null || choice.error != null || choice.index !== void 0 && choice.index !== 0 || choice.finish_reason !== void 0 && choice.finish_reason !== "stop") {
|
|
1748
1908
|
throw new Error("Reviewer API did not return a text assessment");
|
|
1749
1909
|
}
|
|
1750
|
-
if (message.content)
|
|
1910
|
+
if (message.content) {
|
|
1911
|
+
contentStarted = true;
|
|
1912
|
+
diagnose?.("first-content");
|
|
1913
|
+
}
|
|
1751
1914
|
assessment = new StreamingAssessment();
|
|
1752
1915
|
assessment.push(message.content);
|
|
1753
1916
|
if (diagnose && typeof assessment.preview()?.safe === "boolean") diagnose("first-rating");
|
|
1754
1917
|
}
|
|
1755
1918
|
reviewStage(signal, "Assessment validation");
|
|
1756
1919
|
const currentUsage = accounting.current();
|
|
1757
|
-
|
|
1920
|
+
const reportUsage = attempt === 0 ? currentUsage : sumUsage(usage, currentUsage);
|
|
1758
1921
|
try {
|
|
1759
1922
|
let result;
|
|
1760
1923
|
try {
|
|
@@ -1764,20 +1927,26 @@ async function review(evidence, config, signal, fetcher = fetch, environment = p
|
|
|
1764
1927
|
}
|
|
1765
1928
|
if (config.stream) progress("streaming", result);
|
|
1766
1929
|
signal.throwIfAborted();
|
|
1767
|
-
return { ...result, ...
|
|
1930
|
+
return { ...result, ...reportUsage ? { usage: reportUsage } : {} };
|
|
1768
1931
|
} catch (error) {
|
|
1769
1932
|
if (!(error instanceof AssessmentFormatError)) throw error;
|
|
1770
|
-
if (
|
|
1933
|
+
if (corrections++ === config.formatRetries) throw new Error("Reviewer response format invalid after configured attempts");
|
|
1771
1934
|
messages.push(
|
|
1772
1935
|
{ role: "assistant", content: assessment.content },
|
|
1773
1936
|
{ role: "user", content: correctionPrompt(error.message) }
|
|
1774
1937
|
);
|
|
1775
1938
|
}
|
|
1939
|
+
} catch (error) {
|
|
1940
|
+
requestAbort.abort();
|
|
1941
|
+
if (contentStarted) throw error;
|
|
1942
|
+
failed = true;
|
|
1943
|
+
failure = error;
|
|
1776
1944
|
} finally {
|
|
1777
|
-
accounting.finalize();
|
|
1945
|
+
const currentUsage = accounting.finalize();
|
|
1946
|
+
usage = attempt === 0 ? currentUsage : sumUsage(usage, currentUsage);
|
|
1778
1947
|
}
|
|
1948
|
+
if (failed && !await retries.wait(failure, signal)) throw failure;
|
|
1779
1949
|
}
|
|
1780
|
-
throw new Error("Reviewer exhausted attempts");
|
|
1781
1950
|
}
|
|
1782
1951
|
|
|
1783
1952
|
// src/classification.ts
|
|
@@ -1909,7 +2078,7 @@ var clock = {
|
|
|
1909
2078
|
};
|
|
1910
2079
|
var permissionResolved = Object.freeze({ reason: "permission resolved" });
|
|
1911
2080
|
var Controller = class {
|
|
1912
|
-
constructor(evaluate, changed, options = { reviewBash: true, reviewEdits: true }, approval, time = clock, modes, approvalObserver) {
|
|
2081
|
+
constructor(evaluate, changed, options = { reviewBash: true, reviewEdits: true }, approval, time = clock, modes, approvalObserver, ratingObserver) {
|
|
1913
2082
|
this.evaluate = evaluate;
|
|
1914
2083
|
this.changed = changed;
|
|
1915
2084
|
this.options = options;
|
|
@@ -1917,6 +2086,7 @@ var Controller = class {
|
|
|
1917
2086
|
this.time = time;
|
|
1918
2087
|
this.modes = modes;
|
|
1919
2088
|
this.approvalObserver = approvalObserver;
|
|
2089
|
+
this.ratingObserver = ratingObserver;
|
|
1920
2090
|
}
|
|
1921
2091
|
evaluate;
|
|
1922
2092
|
changed;
|
|
@@ -1925,6 +2095,7 @@ var Controller = class {
|
|
|
1925
2095
|
time;
|
|
1926
2096
|
modes;
|
|
1927
2097
|
approvalObserver;
|
|
2098
|
+
ratingObserver;
|
|
1928
2099
|
entries = /* @__PURE__ */ new Map();
|
|
1929
2100
|
stopped = false;
|
|
1930
2101
|
version = 0;
|
|
@@ -2217,12 +2388,19 @@ var Controller = class {
|
|
|
2217
2388
|
this.clearProgress(entry);
|
|
2218
2389
|
if (active()) {
|
|
2219
2390
|
entry.view = assessment ? { ...entry.view, status: "complete", assessment, progress: void 0 } : { ...entry.view, status: "unrelated", progress: void 0 };
|
|
2391
|
+
if (assessment) {
|
|
2392
|
+
try {
|
|
2393
|
+
void Promise.resolve(this.ratingObserver?.(assessment.safe)).catch(() => {
|
|
2394
|
+
});
|
|
2395
|
+
} catch {
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2220
2398
|
this.publish();
|
|
2221
2399
|
}
|
|
2222
2400
|
}, (error) => {
|
|
2223
2401
|
this.clearProgress(entry);
|
|
2224
2402
|
if (active()) {
|
|
2225
|
-
entry.view = { ...entry.view, status: entry.view.status === "identifying" ? "unidentified" : "unavailable", progress: void 0, error: error instanceof Error ? error.message :
|
|
2403
|
+
entry.view = { ...entry.view, status: entry.view.status === "identifying" ? "unidentified" : "unavailable", progress: void 0, error: error instanceof Error ? error.message : uiText.review.failed };
|
|
2226
2404
|
this.publish();
|
|
2227
2405
|
}
|
|
2228
2406
|
});
|
|
@@ -3344,7 +3522,7 @@ async function evaluateEvidence(request, reader2, options, config, configError,
|
|
|
3344
3522
|
if (!classified) return null;
|
|
3345
3523
|
if (!enabledKind(classified.kind, options)) return null;
|
|
3346
3524
|
onIdentified();
|
|
3347
|
-
if (!config) throw new Error(configError ||
|
|
3525
|
+
if (!config) throw new Error(configError || uiText.review.invalidConfiguration);
|
|
3348
3526
|
const scope = files.scope(signal);
|
|
3349
3527
|
if (classified.kind === "shell") {
|
|
3350
3528
|
const context = await loadContext(request, reader2, signal, scope, invocation);
|
|
@@ -3467,12 +3645,25 @@ import { constants as constants3 } from "node:fs";
|
|
|
3467
3645
|
import { mkdir, open as open3, readdir as readdir2, rename, unlink } from "node:fs/promises";
|
|
3468
3646
|
import { randomUUID } from "node:crypto";
|
|
3469
3647
|
import path8 from "node:path";
|
|
3470
|
-
var empty = () => ({
|
|
3648
|
+
var empty = () => ({
|
|
3649
|
+
requests: 0,
|
|
3650
|
+
tokenRequests: 0,
|
|
3651
|
+
input: 0,
|
|
3652
|
+
output: 0,
|
|
3653
|
+
priced: 0,
|
|
3654
|
+
cost: 0,
|
|
3655
|
+
since: null,
|
|
3656
|
+
safe: 0,
|
|
3657
|
+
unsafe: 0,
|
|
3658
|
+
ratingsSince: null
|
|
3659
|
+
});
|
|
3471
3660
|
var count2 = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3472
3661
|
var amount = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
3473
3662
|
var snapshotName = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}\.json$/;
|
|
3474
3663
|
function validate(value) {
|
|
3475
|
-
if (!value || ![value.requests, value.tokenRequests, value.input, value.output, value.priced].every(count2) || !amount(value.cost) || value.priced > value.requests || value.tokenRequests > value.requests || value.requests > value.tokenRequests + value.priced || !value.tokenRequests && (value.input !== 0 || value.output !== 0) || (value.requests > 0 ? !count2(value.since) || value.since > 864e13 : value.since !== null || value.input !== 0 || value.output !== 0) || !value.priced && value.cost !== 0
|
|
3664
|
+
if (!value || ![value.requests, value.tokenRequests, value.input, value.output, value.priced, value.safe, value.unsafe, value.safe + value.unsafe].every(count2) || !amount(value.cost) || value.priced > value.requests || value.tokenRequests > value.requests || value.requests > value.tokenRequests + value.priced || !value.tokenRequests && (value.input !== 0 || value.output !== 0) || (value.requests > 0 ? !count2(value.since) || value.since > 864e13 : value.since !== null || value.input !== 0 || value.output !== 0) || !value.priced && value.cost !== 0 || (value.safe + value.unsafe > 0 ? !count2(value.ratingsSince) || value.ratingsSince > 864e13 : value.ratingsSince !== null)) {
|
|
3665
|
+
throw new Error("Invalid lifetime usage totals");
|
|
3666
|
+
}
|
|
3476
3667
|
return value;
|
|
3477
3668
|
}
|
|
3478
3669
|
function add(left, right) {
|
|
@@ -3483,28 +3674,32 @@ function add(left, right) {
|
|
|
3483
3674
|
tokenRequests: left.tokenRequests + right.tokenRequests,
|
|
3484
3675
|
priced: left.priced + right.priced,
|
|
3485
3676
|
cost: left.cost + right.cost,
|
|
3486
|
-
since: left.since === null ? right.since : right.since === null ? left.since : Math.min(left.since, right.since)
|
|
3677
|
+
since: left.since === null ? right.since : right.since === null ? left.since : Math.min(left.since, right.since),
|
|
3678
|
+
safe: left.safe + right.safe,
|
|
3679
|
+
unsafe: left.unsafe + right.unsafe,
|
|
3680
|
+
ratingsSince: left.ratingsSince === null ? right.ratingsSince : right.ratingsSince === null ? left.ratingsSince : Math.min(left.ratingsSince, right.ratingsSince)
|
|
3487
3681
|
});
|
|
3488
3682
|
}
|
|
3489
3683
|
var LifetimeUsage = class {
|
|
3490
|
-
constructor(directory,
|
|
3684
|
+
constructor(directory, ...legacyDirectories) {
|
|
3491
3685
|
this.directory = directory;
|
|
3492
|
-
|
|
3493
|
-
if (!path8.isAbsolute(directory) || legacyDirectory !== void 0 && !path8.isAbsolute(legacyDirectory)) {
|
|
3686
|
+
if (![directory, ...legacyDirectories].every((value) => path8.isAbsolute(value))) {
|
|
3494
3687
|
throw new Error("Lifetime usage directory must be absolute");
|
|
3495
3688
|
}
|
|
3689
|
+
this.legacyDirectories = legacyDirectories;
|
|
3496
3690
|
}
|
|
3497
3691
|
directory;
|
|
3498
|
-
legacyDirectory;
|
|
3499
3692
|
id = randomUUID();
|
|
3500
3693
|
local = empty();
|
|
3501
3694
|
pending = Promise.resolve();
|
|
3695
|
+
legacyDirectories;
|
|
3502
3696
|
record(usage) {
|
|
3503
3697
|
const tokens = count2(usage.input) && count2(usage.output);
|
|
3504
3698
|
if (!tokens && (usage.input !== void 0 || usage.output !== void 0 || usage.cost === void 0) || usage.cost !== void 0 && !amount(usage.cost)) {
|
|
3505
3699
|
return Promise.reject(new Error("Invalid request usage"));
|
|
3506
3700
|
}
|
|
3507
|
-
|
|
3701
|
+
return this.increment({
|
|
3702
|
+
...empty(),
|
|
3508
3703
|
requests: 1,
|
|
3509
3704
|
tokenRequests: tokens ? 1 : 0,
|
|
3510
3705
|
input: usage.input ?? 0,
|
|
@@ -3512,7 +3707,14 @@ var LifetimeUsage = class {
|
|
|
3512
3707
|
priced: usage.cost === void 0 ? 0 : 1,
|
|
3513
3708
|
cost: usage.cost ?? 0,
|
|
3514
3709
|
since: Date.now()
|
|
3515
|
-
};
|
|
3710
|
+
});
|
|
3711
|
+
}
|
|
3712
|
+
/** One accepted final review, independent of POST count or received usage. */
|
|
3713
|
+
recordRating(safe) {
|
|
3714
|
+
if (typeof safe !== "boolean") return Promise.reject(new Error("Invalid review rating"));
|
|
3715
|
+
return this.increment({ ...empty(), safe: safe ? 1 : 0, unsafe: safe ? 0 : 1, ratingsSince: Date.now() });
|
|
3716
|
+
}
|
|
3717
|
+
increment(increment) {
|
|
3516
3718
|
this.pending = this.pending.catch(() => {
|
|
3517
3719
|
}).then(async () => {
|
|
3518
3720
|
this.local = add(this.local, increment);
|
|
@@ -3521,7 +3723,7 @@ var LifetimeUsage = class {
|
|
|
3521
3723
|
try {
|
|
3522
3724
|
const file = await open3(temporary, "wx", 384);
|
|
3523
3725
|
try {
|
|
3524
|
-
await file.writeFile(JSON.stringify({ version:
|
|
3726
|
+
await file.writeFile(JSON.stringify({ version: 3, ...this.local }));
|
|
3525
3727
|
await file.sync();
|
|
3526
3728
|
} finally {
|
|
3527
3729
|
await file.close();
|
|
@@ -3549,7 +3751,7 @@ var LifetimeUsage = class {
|
|
|
3549
3751
|
await this.flush();
|
|
3550
3752
|
signal?.throwIfAborted();
|
|
3551
3753
|
let total = empty();
|
|
3552
|
-
const directories = new Set([this.directory, ...this.
|
|
3754
|
+
const directories = new Set([this.directory, ...this.legacyDirectories].map((directory) => path8.resolve(directory)));
|
|
3553
3755
|
for (const directory of directories) total = add(total, await this.readDirectory(directory, signal));
|
|
3554
3756
|
signal?.throwIfAborted();
|
|
3555
3757
|
return total;
|
|
@@ -3581,7 +3783,7 @@ var LifetimeUsage = class {
|
|
|
3581
3783
|
}
|
|
3582
3784
|
if (size > 1024) throw new Error("Invalid lifetime usage snapshot");
|
|
3583
3785
|
const value = JSON.parse(bytes.subarray(0, size).toString("utf8"));
|
|
3584
|
-
if (value?.version !== 1 && value?.version !== 2) throw new Error("Unsupported lifetime usage snapshot");
|
|
3786
|
+
if (value?.version !== 1 && value?.version !== 2 && value?.version !== 3) throw new Error("Unsupported lifetime usage snapshot");
|
|
3585
3787
|
total = add(total, validate({
|
|
3586
3788
|
requests: value.requests,
|
|
3587
3789
|
input: value.input,
|
|
@@ -3589,7 +3791,10 @@ var LifetimeUsage = class {
|
|
|
3589
3791
|
tokenRequests: value.version === 1 ? value.requests : value.tokenRequests,
|
|
3590
3792
|
priced: value.priced,
|
|
3591
3793
|
cost: value.cost,
|
|
3592
|
-
since: value.since
|
|
3794
|
+
since: value.since,
|
|
3795
|
+
safe: value.version === 3 ? value.safe : 0,
|
|
3796
|
+
unsafe: value.version === 3 ? value.unsafe : 0,
|
|
3797
|
+
ratingsSince: value.version === 3 ? value.ratingsSince : null
|
|
3593
3798
|
}));
|
|
3594
3799
|
} finally {
|
|
3595
3800
|
await file.close();
|
|
@@ -3601,20 +3806,24 @@ var LifetimeUsage = class {
|
|
|
3601
3806
|
}
|
|
3602
3807
|
};
|
|
3603
3808
|
function lifetimeCost(totals) {
|
|
3604
|
-
if (!totals.requests) return
|
|
3605
|
-
if (!totals.priced) return
|
|
3606
|
-
return
|
|
3809
|
+
if (!totals.requests) return uiText.lifetime.empty;
|
|
3810
|
+
if (!totals.priced) return uiText.lifetime.costUnavailable;
|
|
3811
|
+
return uiText.lifetime.cost(totals.cost.toFixed(4), totals.priced < totals.requests);
|
|
3607
3812
|
}
|
|
3608
3813
|
function lifetimeReport(totals) {
|
|
3609
3814
|
return [
|
|
3610
3815
|
lifetimeCost(totals),
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3816
|
+
uiText.lifetime.ratings(totals.safe, totals.unsafe),
|
|
3817
|
+
uiText.lifetime.requests(totals.requests),
|
|
3818
|
+
totals.tokenRequests ? uiText.lifetime.tokens(totals.input, totals.output, totals.tokenRequests < totals.requests) : uiText.lifetime.tokensUnavailable,
|
|
3819
|
+
uiText.lifetime.tokenCoverage(totals.tokenRequests, totals.requests),
|
|
3820
|
+
uiText.lifetime.pricingCoverage(totals.priced, totals.requests),
|
|
3821
|
+
...totals.since === null ? [] : [uiText.lifetime.since(new Date(totals.since).toISOString().slice(0, 10))],
|
|
3822
|
+
...totals.ratingsSince === null ? [] : [uiText.lifetime.ratingsSince(new Date(totals.ratingsSince).toISOString().slice(0, 10))],
|
|
3823
|
+
"",
|
|
3824
|
+
uiText.lifetime.explanation,
|
|
3616
3825
|
"",
|
|
3617
|
-
|
|
3826
|
+
uiText.lifetime.ratingsExplanation
|
|
3618
3827
|
].join("\n");
|
|
3619
3828
|
}
|
|
3620
3829
|
|
|
@@ -3700,7 +3909,7 @@ var LifetimeRefresh = class {
|
|
|
3700
3909
|
// src/lifetime-view.tsx
|
|
3701
3910
|
function lifetimeTracker(api) {
|
|
3702
3911
|
const directory = path9.join(api.state.path.state, "opencode-reviewer");
|
|
3703
|
-
const store = new LifetimeUsage(path9.join(directory, "usage-v2"), path9.join(directory, "usage-v1"));
|
|
3912
|
+
const store = new LifetimeUsage(path9.join(directory, "usage-v3"), path9.join(directory, "usage-v2"), path9.join(directory, "usage-v1"));
|
|
3704
3913
|
const [totals, setTotals] = createSignal();
|
|
3705
3914
|
const [unavailable, setUnavailable] = createSignal(false);
|
|
3706
3915
|
const refresh = new LifetimeRefresh((signal) => store.totals(signal), (value) => {
|
|
@@ -3713,14 +3922,16 @@ function lifetimeTracker(api) {
|
|
|
3713
3922
|
commands: [{
|
|
3714
3923
|
name: "opencode-reviewer.lifetime",
|
|
3715
3924
|
namespace: "palette",
|
|
3716
|
-
title:
|
|
3717
|
-
category:
|
|
3925
|
+
title: uiText.commands.lifetime,
|
|
3926
|
+
category: uiText.commands.category,
|
|
3718
3927
|
run: () => {
|
|
3719
3928
|
refresh.refresh();
|
|
3720
3929
|
api.ui.dialog.replace(() => _$createComponent(api.ui.DialogAlert, {
|
|
3721
|
-
title
|
|
3930
|
+
get title() {
|
|
3931
|
+
return uiText.lifetime.title;
|
|
3932
|
+
},
|
|
3722
3933
|
get message() {
|
|
3723
|
-
return _$memo(() => !!unavailable())() ?
|
|
3934
|
+
return _$memo(() => !!unavailable())() ? uiText.lifetime.unavailable : _$memo(() => !!totals())() ? lifetimeReport(totals()) : uiText.lifetime.loading;
|
|
3724
3935
|
}
|
|
3725
3936
|
}));
|
|
3726
3937
|
}
|
|
@@ -3735,9 +3946,12 @@ function lifetimeTracker(api) {
|
|
|
3735
3946
|
// The controller must settle aborted review workers before this final queue drain.
|
|
3736
3947
|
flush: () => store.flush().catch(() => {
|
|
3737
3948
|
}),
|
|
3738
|
-
text: () => unavailable() ?
|
|
3949
|
+
text: () => unavailable() ? uiText.lifetime.inlineUnavailable : totals()?.requests ? [lifetimeCost(totals()), uiText.lifetime.ratings(totals().safe, totals().unsafe)].join("\n") : void 0,
|
|
3739
3950
|
record: (usage) => {
|
|
3740
3951
|
void store.record(usage).then(() => refresh.refresh(), () => refresh.failed());
|
|
3952
|
+
},
|
|
3953
|
+
recordRating: (safe) => {
|
|
3954
|
+
void store.recordRating(safe).then(() => refresh.refresh(), () => refresh.failed());
|
|
3741
3955
|
}
|
|
3742
3956
|
};
|
|
3743
3957
|
}
|
|
@@ -3939,8 +4153,8 @@ function sessionModeCommands(api, modes, changed) {
|
|
|
3939
4153
|
name: `opencode-reviewer.${enabled ? "enable" : "disable"}`,
|
|
3940
4154
|
namespace: "palette",
|
|
3941
4155
|
slashName: `reviewer-${enabled ? "enable" : "disable"}`,
|
|
3942
|
-
title:
|
|
3943
|
-
category:
|
|
4156
|
+
title: enabled ? uiText.commands.enable : uiText.commands.disable,
|
|
4157
|
+
category: uiText.commands.category,
|
|
3944
4158
|
enabled: () => selected() !== void 0,
|
|
3945
4159
|
run: async () => {
|
|
3946
4160
|
const sessionID = selected();
|
|
@@ -3961,10 +4175,10 @@ function sessionModeCommands(api, modes, changed) {
|
|
|
3961
4175
|
await persisted;
|
|
3962
4176
|
if (!api.lifecycle.signal.aborted && latest.get(root) === order) api.ui.toast({
|
|
3963
4177
|
variant: "success",
|
|
3964
|
-
message:
|
|
4178
|
+
message: uiText.sessionMode.saved(enabled)
|
|
3965
4179
|
});
|
|
3966
4180
|
} catch {
|
|
3967
|
-
if (!api.lifecycle.signal.aborted && (root === void 0 || latest.get(root) === order)) api.ui.toast({ variant: "error", message: applied ?
|
|
4181
|
+
if (!api.lifecycle.signal.aborted && (root === void 0 || latest.get(root) === order)) api.ui.toast({ variant: "error", message: applied ? uiText.sessionMode.saveFailed(enabled) : uiText.sessionMode.unavailable });
|
|
3968
4182
|
}
|
|
3969
4183
|
}
|
|
3970
4184
|
})) });
|
|
@@ -3985,7 +4199,7 @@ var notificationClock = {
|
|
|
3985
4199
|
}
|
|
3986
4200
|
};
|
|
3987
4201
|
function notificationText(value) {
|
|
3988
|
-
return Array.from(value.replace(/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/g, " ")).slice(0, 256).join("").trim() ||
|
|
4202
|
+
return Array.from(value.replace(/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/g, " ")).slice(0, 256).join("").trim() || uiText.notifications.fallbackSession;
|
|
3989
4203
|
}
|
|
3990
4204
|
function notificationMarkup(value) {
|
|
3991
4205
|
return notificationText(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -3993,16 +4207,14 @@ function notificationMarkup(value) {
|
|
|
3993
4207
|
|
|
3994
4208
|
// src/notification-policy.ts
|
|
3995
4209
|
var NotificationPolicy = class {
|
|
3996
|
-
constructor(config, auto,
|
|
4210
|
+
constructor(config, auto, backend, clock2 = notificationClock) {
|
|
3997
4211
|
this.config = config;
|
|
3998
4212
|
this.auto = auto;
|
|
3999
|
-
this.delay = delay;
|
|
4000
4213
|
this.backend = backend;
|
|
4001
4214
|
this.clock = clock2;
|
|
4002
4215
|
}
|
|
4003
4216
|
config;
|
|
4004
4217
|
auto;
|
|
4005
|
-
delay;
|
|
4006
4218
|
backend;
|
|
4007
4219
|
clock;
|
|
4008
4220
|
permissions = /* @__PURE__ */ new Map();
|
|
@@ -4082,7 +4294,7 @@ var NotificationPolicy = class {
|
|
|
4082
4294
|
if (entry.phase === "attention") return;
|
|
4083
4295
|
this.withdraw(entry.banner);
|
|
4084
4296
|
entry.phase = "attention";
|
|
4085
|
-
entry.banner = this.show("attention",
|
|
4297
|
+
entry.banner = this.show("attention", uiText.notifications.attention, entry.target);
|
|
4086
4298
|
}
|
|
4087
4299
|
snapshot(views) {
|
|
4088
4300
|
if (this.stopped) return;
|
|
@@ -4103,7 +4315,7 @@ var NotificationPolicy = class {
|
|
|
4103
4315
|
if (entry.phase !== "automatic") {
|
|
4104
4316
|
this.withdraw(entry.banner);
|
|
4105
4317
|
entry.phase = "automatic";
|
|
4106
|
-
entry.banner =
|
|
4318
|
+
entry.banner = void 0;
|
|
4107
4319
|
}
|
|
4108
4320
|
return;
|
|
4109
4321
|
}
|
|
@@ -4135,7 +4347,7 @@ var NotificationPolicy = class {
|
|
|
4135
4347
|
approved(request, target) {
|
|
4136
4348
|
if (!this.remember(`approved:${request.id}`)) return;
|
|
4137
4349
|
const current = this.permissions.get(request.id);
|
|
4138
|
-
if (current || target) this.show("approved",
|
|
4350
|
+
if (current || target) this.show("approved", uiText.notifications.approved, current?.target ?? target);
|
|
4139
4351
|
}
|
|
4140
4352
|
resolved(kind, id) {
|
|
4141
4353
|
const entries = kind === "permission" ? this.permissions : this.questions;
|
|
@@ -4146,7 +4358,7 @@ var NotificationPolicy = class {
|
|
|
4146
4358
|
this.remember(`${kind}:${id}`);
|
|
4147
4359
|
}
|
|
4148
4360
|
turn(kind, id, target) {
|
|
4149
|
-
if (this.remember(`turn:${target.root}:${id}`)) this.show(kind, kind
|
|
4361
|
+
if (this.remember(`turn:${target.root}:${id}`)) this.show(kind, uiText.notifications[kind], target);
|
|
4150
4362
|
}
|
|
4151
4363
|
deleted(sessionID) {
|
|
4152
4364
|
for (const banner of this.banners) if (banner.target.root === sessionID || banner.target.sessionID === sessionID) this.withdraw(banner);
|
|
@@ -4313,7 +4525,7 @@ var NotificationHost = class {
|
|
|
4313
4525
|
}
|
|
4314
4526
|
const root = this.roots.get(rootID);
|
|
4315
4527
|
if (!root || request.received < root.since) return;
|
|
4316
|
-
const target = { root: rootID, sessionID: rootID, title: this.api.state.session.get(rootID)?.title ??
|
|
4528
|
+
const target = { root: rootID, sessionID: rootID, title: this.api.state.session.get(rootID)?.title ?? uiText.notifications.fallbackSession };
|
|
4317
4529
|
request.target = target;
|
|
4318
4530
|
if (kind === "question") this.policy.question(id, target, true);
|
|
4319
4531
|
else {
|
|
@@ -4327,7 +4539,7 @@ var NotificationHost = class {
|
|
|
4327
4539
|
if (this.stopped) return;
|
|
4328
4540
|
this.views = views;
|
|
4329
4541
|
for (const request of this.permissions.values()) if (request.target) {
|
|
4330
|
-
request.target.title = this.api.state.session.get(request.target.root)?.title ??
|
|
4542
|
+
request.target.title = this.api.state.session.get(request.target.root)?.title ?? uiText.notifications.fallbackSession;
|
|
4331
4543
|
}
|
|
4332
4544
|
this.policy.snapshot(views);
|
|
4333
4545
|
for (const [id, request] of this.permissions) this.admit("permission", id, request);
|
|
@@ -4343,7 +4555,7 @@ var NotificationHost = class {
|
|
|
4343
4555
|
const dispatched = this.dispatched.get(fact.request.id);
|
|
4344
4556
|
this.dispatched.delete(fact.request.id);
|
|
4345
4557
|
if (dispatched?.automatic) {
|
|
4346
|
-
dispatched.target.title = this.api.state.session.get(dispatched.target.root)?.title ??
|
|
4558
|
+
dispatched.target.title = this.api.state.session.get(dispatched.target.root)?.title ?? uiText.notifications.fallbackSession;
|
|
4347
4559
|
this.policy.approved(fact.request, dispatched.target);
|
|
4348
4560
|
}
|
|
4349
4561
|
} else this.dispatched.delete(fact.request.id);
|
|
@@ -4392,7 +4604,7 @@ var NotificationHost = class {
|
|
|
4392
4604
|
if (assistant?.role === "assistant") root.assistant = assistant;
|
|
4393
4605
|
if (root.assistant?.error?.name === "MessageAbortedError" || root.error?.name === "MessageAbortedError") return;
|
|
4394
4606
|
if (this.views.some((v) => this.rootOf(v.request.sessionID) === id) || [...this.questions.values()].some((q) => this.rootOf(q.sessionID) === id) || this.api.state.session.question(id).length || this.api.state.session.permission(id).length) return;
|
|
4395
|
-
const target = { root: id, sessionID: id, title: this.api.state.session.get(id)?.title ??
|
|
4607
|
+
const target = { root: id, sessionID: id, title: this.api.state.session.get(id)?.title ?? uiText.notifications.fallbackSession };
|
|
4396
4608
|
const message = root.assistant;
|
|
4397
4609
|
if (message?.error || root.error && (!message?.time.completed || !message.finish || message.finish === "error")) {
|
|
4398
4610
|
this.policy.turn("error", root.user ?? root.error.id, target);
|
|
@@ -5712,7 +5924,7 @@ var LinuxNotifications = class {
|
|
|
5712
5924
|
const process2 = this.start(
|
|
5713
5925
|
"notify-send",
|
|
5714
5926
|
[
|
|
5715
|
-
|
|
5927
|
+
`--app-name=${uiText.notifications.application}`,
|
|
5716
5928
|
...icon ? [`--icon=${icon}`] : [],
|
|
5717
5929
|
// GNOME uses the desktop-entry's source icon, ignoring --icon there.
|
|
5718
5930
|
// image-path supplies the separate image beside the notification heading.
|
|
@@ -5724,13 +5936,12 @@ var LinuxNotifications = class {
|
|
|
5724
5936
|
"--print-id",
|
|
5725
5937
|
// GNOME emits an activation token only for a recognized application.
|
|
5726
5938
|
...this.identity ? ["--hint=string:desktop-entry:org.gnome.Terminal"] : [],
|
|
5727
|
-
...this.identity ? [
|
|
5939
|
+
...this.identity ? [`--action=default=${uiText.notifications.openSession}`] : [],
|
|
5728
5940
|
"--wait",
|
|
5729
5941
|
"--",
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
`${notificationMarkup(message.title)} \xB7 ${notificationMarkup(message.body)}`.replaceAll("\\", "\\\\")
|
|
5942
|
+
// Summaries are plain text; only the body supports desktop markup.
|
|
5943
|
+
uiText.notifications.heading(notificationText(message.body)).replaceAll("\\", "\\\\"),
|
|
5944
|
+
uiText.notifications.body(notificationMarkup(message.title)).replaceAll("\\", "\\\\")
|
|
5734
5945
|
],
|
|
5735
5946
|
12e4,
|
|
5736
5947
|
AbortSignal.any([local.signal, this.abort.signal]),
|
|
@@ -5795,13 +6006,16 @@ function ReviewLoading(props) {
|
|
|
5795
6006
|
onCleanup(() => clearInterval(timer));
|
|
5796
6007
|
});
|
|
5797
6008
|
return (() => {
|
|
5798
|
-
var _el$ = _$createElement("text");
|
|
6009
|
+
var _el$ = _$createElement("text"), _el$2 = _$createTextNode(` `);
|
|
6010
|
+
_$insertNode(_el$, _el$2);
|
|
5799
6011
|
_$setProp(_el$, "height", 1);
|
|
5800
6012
|
_$insert(_el$, _$createComponent2(Show, {
|
|
5801
6013
|
get when() {
|
|
5802
6014
|
return animated();
|
|
5803
6015
|
},
|
|
5804
|
-
fallback
|
|
6016
|
+
get fallback() {
|
|
6017
|
+
return uiText.review.staticIndicator;
|
|
6018
|
+
},
|
|
5805
6019
|
get children() {
|
|
5806
6020
|
return _$createComponent2(Index, {
|
|
5807
6021
|
get each() {
|
|
@@ -5817,18 +6031,21 @@ function ReviewLoading(props) {
|
|
|
5817
6031
|
return RGBA.fromValues(Math.min(1, base.r * brightness), Math.min(1, base.g * brightness), Math.min(1, base.b * brightness), alpha);
|
|
5818
6032
|
});
|
|
5819
6033
|
return (() => {
|
|
5820
|
-
var _el$
|
|
5821
|
-
_$insert(_el$
|
|
5822
|
-
_$effect((_$p) => _$setProp(_el$
|
|
6034
|
+
var _el$3 = _$createElement("span");
|
|
6035
|
+
_$insert(_el$3, () => cell().character);
|
|
6036
|
+
_$effect((_$p) => _$setProp(_el$3, "style", {
|
|
5823
6037
|
fg: color()
|
|
5824
6038
|
}, _$p));
|
|
5825
|
-
return _el$
|
|
6039
|
+
return _el$3;
|
|
5826
6040
|
})();
|
|
5827
6041
|
}
|
|
5828
6042
|
});
|
|
5829
6043
|
}
|
|
5830
|
-
}),
|
|
5831
|
-
_$insert(_el$, () =>
|
|
6044
|
+
}), _el$2);
|
|
6045
|
+
_$insert(_el$, (() => {
|
|
6046
|
+
var _c$ = _$memo2(() => !!props.retrying);
|
|
6047
|
+
return () => _c$() ? uiText.review.retrying : uiText.review.evaluating;
|
|
6048
|
+
})(), null);
|
|
5832
6049
|
_$effect((_$p) => _$setProp(_el$, "fg", props.api.theme.current.textMuted, _$p));
|
|
5833
6050
|
return _el$;
|
|
5834
6051
|
})();
|
|
@@ -5843,21 +6060,21 @@ function ReviewDescription(props) {
|
|
|
5843
6060
|
return syntax;
|
|
5844
6061
|
});
|
|
5845
6062
|
return (() => {
|
|
5846
|
-
var _el$
|
|
6063
|
+
var _el$4 = _$createElement("markdown");
|
|
5847
6064
|
var _ref$ = props.ref;
|
|
5848
|
-
typeof _ref$ === "function" ? _$use(_ref$, _el$
|
|
5849
|
-
_$setProp(_el$
|
|
5850
|
-
_$setProp(_el$
|
|
6065
|
+
typeof _ref$ === "function" ? _$use(_ref$, _el$4) : props.ref = _el$4;
|
|
6066
|
+
_$setProp(_el$4, "conceal", true);
|
|
6067
|
+
_$setProp(_el$4, "tableOptions", {
|
|
5851
6068
|
style: "grid"
|
|
5852
6069
|
});
|
|
5853
|
-
_$setProp(_el$
|
|
5854
|
-
_$setProp(_el$
|
|
6070
|
+
_$setProp(_el$4, "width", "100%");
|
|
6071
|
+
_$setProp(_el$4, "flexShrink", 0);
|
|
5855
6072
|
_$effect((_p$) => {
|
|
5856
6073
|
var _v$ = displayText(props.text), _v$2 = style(), _v$3 = props.api.theme.current.markdownText, _v$4 = props.streaming;
|
|
5857
|
-
_v$ !== _p$.e && (_p$.e = _$setProp(_el$
|
|
5858
|
-
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
5859
|
-
_v$3 !== _p$.a && (_p$.a = _$setProp(_el$
|
|
5860
|
-
_v$4 !== _p$.o && (_p$.o = _$setProp(_el$
|
|
6074
|
+
_v$ !== _p$.e && (_p$.e = _$setProp(_el$4, "content", _v$, _p$.e));
|
|
6075
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp(_el$4, "syntaxStyle", _v$2, _p$.t));
|
|
6076
|
+
_v$3 !== _p$.a && (_p$.a = _$setProp(_el$4, "fg", _v$3, _p$.a));
|
|
6077
|
+
_v$4 !== _p$.o && (_p$.o = _$setProp(_el$4, "streaming", _v$4, _p$.o));
|
|
5861
6078
|
return _p$;
|
|
5862
6079
|
}, {
|
|
5863
6080
|
e: void 0,
|
|
@@ -5865,7 +6082,7 @@ function ReviewDescription(props) {
|
|
|
5865
6082
|
a: void 0,
|
|
5866
6083
|
o: void 0
|
|
5867
6084
|
});
|
|
5868
|
-
return _el$
|
|
6085
|
+
return _el$4;
|
|
5869
6086
|
})();
|
|
5870
6087
|
}
|
|
5871
6088
|
function highlightingComplete(node) {
|
|
@@ -5888,28 +6105,28 @@ function ReviewButton(props) {
|
|
|
5888
6105
|
return 0.299 * r + 0.587 * g + 0.114 * b > 0.5 ? RGBA.fromInts(0, 0, 0) : RGBA.fromInts(255, 255, 255);
|
|
5889
6106
|
};
|
|
5890
6107
|
return (() => {
|
|
5891
|
-
var _el$
|
|
5892
|
-
_$insertNode(_el$
|
|
5893
|
-
_$setProp(_el$
|
|
5894
|
-
_$setProp(_el$
|
|
5895
|
-
_$setProp(_el$
|
|
6108
|
+
var _el$5 = _$createElement("box"), _el$6 = _$createElement("text");
|
|
6109
|
+
_$insertNode(_el$5, _el$6);
|
|
6110
|
+
_$setProp(_el$5, "paddingLeft", 1);
|
|
6111
|
+
_$setProp(_el$5, "paddingRight", 1);
|
|
6112
|
+
_$setProp(_el$5, "onMouseOver", () => {
|
|
5896
6113
|
if (!props.disabled) props.onHover?.();
|
|
5897
6114
|
});
|
|
5898
|
-
_$setProp(_el$
|
|
6115
|
+
_$setProp(_el$5, "onMouseUp", (event) => {
|
|
5899
6116
|
event.stopPropagation();
|
|
5900
6117
|
if (event.button === 0 && !props.disabled) props.onClick();
|
|
5901
6118
|
});
|
|
5902
|
-
_$insert(_el$
|
|
6119
|
+
_$insert(_el$6, () => props.label);
|
|
5903
6120
|
_$effect((_p$) => {
|
|
5904
6121
|
var _v$5 = selected() ? props.api.theme.current.warning : props.api.theme.current.backgroundMenu, _v$6 = foreground();
|
|
5905
|
-
_v$5 !== _p$.e && (_p$.e = _$setProp(_el$
|
|
5906
|
-
_v$6 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
6122
|
+
_v$5 !== _p$.e && (_p$.e = _$setProp(_el$5, "backgroundColor", _v$5, _p$.e));
|
|
6123
|
+
_v$6 !== _p$.t && (_p$.t = _$setProp(_el$6, "fg", _v$6, _p$.t));
|
|
5907
6124
|
return _p$;
|
|
5908
6125
|
}, {
|
|
5909
6126
|
e: void 0,
|
|
5910
6127
|
t: void 0
|
|
5911
6128
|
});
|
|
5912
|
-
return _el$
|
|
6129
|
+
return _el$5;
|
|
5913
6130
|
})();
|
|
5914
6131
|
}
|
|
5915
6132
|
function ReviewFooter(props) {
|
|
@@ -5917,30 +6134,30 @@ function ReviewFooter(props) {
|
|
|
5917
6134
|
const state = () => props.view.autoApproval;
|
|
5918
6135
|
const label = () => {
|
|
5919
6136
|
const current = state();
|
|
5920
|
-
return current?.status === "countdown" ?
|
|
6137
|
+
return current?.status === "countdown" ? uiText.autoApproval.countdown(current.seconds) : uiText.autoApproval.checking;
|
|
5921
6138
|
};
|
|
5922
6139
|
return _$createComponent2(Show, {
|
|
5923
6140
|
get when() {
|
|
5924
6141
|
return _$memo2(() => !!props.enabled)() && props.view.assessment?.safe;
|
|
5925
6142
|
},
|
|
5926
6143
|
get children() {
|
|
5927
|
-
var _el$
|
|
5928
|
-
_$setProp(_el$
|
|
5929
|
-
_$setProp(_el$
|
|
5930
|
-
_$setProp(_el$
|
|
5931
|
-
_$setProp(_el$
|
|
5932
|
-
_$setProp(_el$
|
|
5933
|
-
_$insert(_el$
|
|
6144
|
+
var _el$7 = _$createElement("box");
|
|
6145
|
+
_$setProp(_el$7, "marginTop", 1);
|
|
6146
|
+
_$setProp(_el$7, "paddingTop", 1);
|
|
6147
|
+
_$setProp(_el$7, "minHeight", 3);
|
|
6148
|
+
_$setProp(_el$7, "flexShrink", 0);
|
|
6149
|
+
_$setProp(_el$7, "border", ["top"]);
|
|
6150
|
+
_$insert(_el$7, _$createComponent2(Switch, {
|
|
5934
6151
|
get children() {
|
|
5935
6152
|
return [_$createComponent2(Match, {
|
|
5936
6153
|
get when() {
|
|
5937
6154
|
return state()?.status === "countdown" || state()?.status === "checking";
|
|
5938
6155
|
},
|
|
5939
6156
|
get children() {
|
|
5940
|
-
var _el$
|
|
5941
|
-
_$setProp(_el$
|
|
5942
|
-
_$setProp(_el$
|
|
5943
|
-
_$insert(_el$
|
|
6157
|
+
var _el$8 = _$createElement("box");
|
|
6158
|
+
_$setProp(_el$8, "flexDirection", "row");
|
|
6159
|
+
_$setProp(_el$8, "gap", 1);
|
|
6160
|
+
_$insert(_el$8, _$createComponent2(ReviewButton, {
|
|
5944
6161
|
get api() {
|
|
5945
6162
|
return props.api;
|
|
5946
6163
|
},
|
|
@@ -5958,18 +6175,20 @@ function ReviewFooter(props) {
|
|
|
5958
6175
|
void props.controller.approveNow(props.view.request.id);
|
|
5959
6176
|
}
|
|
5960
6177
|
}), null);
|
|
5961
|
-
_$insert(_el$
|
|
6178
|
+
_$insert(_el$8, _$createComponent2(ReviewButton, {
|
|
5962
6179
|
get api() {
|
|
5963
6180
|
return props.api;
|
|
5964
6181
|
},
|
|
5965
|
-
label
|
|
6182
|
+
get label() {
|
|
6183
|
+
return uiText.autoApproval.cancel;
|
|
6184
|
+
},
|
|
5966
6185
|
get selected() {
|
|
5967
6186
|
return selected() === "cancel";
|
|
5968
6187
|
},
|
|
5969
6188
|
onHover: () => setSelected("cancel"),
|
|
5970
6189
|
onClick: () => props.controller.cancelAutoApproval(props.view.request.id)
|
|
5971
6190
|
}), null);
|
|
5972
|
-
return _el$
|
|
6191
|
+
return _el$8;
|
|
5973
6192
|
}
|
|
5974
6193
|
}), _$createComponent2(Match, {
|
|
5975
6194
|
get when() {
|
|
@@ -5980,7 +6199,9 @@ function ReviewFooter(props) {
|
|
|
5980
6199
|
get api() {
|
|
5981
6200
|
return props.api;
|
|
5982
6201
|
},
|
|
5983
|
-
label
|
|
6202
|
+
get label() {
|
|
6203
|
+
return uiText.autoApproval.allowing;
|
|
6204
|
+
},
|
|
5984
6205
|
disabled: true,
|
|
5985
6206
|
onClick: () => {
|
|
5986
6207
|
}
|
|
@@ -5991,10 +6212,10 @@ function ReviewFooter(props) {
|
|
|
5991
6212
|
return state()?.status === "cancelled";
|
|
5992
6213
|
},
|
|
5993
6214
|
get children() {
|
|
5994
|
-
var _el$
|
|
5995
|
-
_$
|
|
5996
|
-
_$effect((_$p) => _$setProp(_el$
|
|
5997
|
-
return _el$
|
|
6215
|
+
var _el$9 = _$createElement("text");
|
|
6216
|
+
_$insert(_el$9, () => uiText.autoApproval.cancelled);
|
|
6217
|
+
_$effect((_$p) => _$setProp(_el$9, "fg", props.api.theme.current.textMuted, _$p));
|
|
6218
|
+
return _el$9;
|
|
5998
6219
|
}
|
|
5999
6220
|
}), _$createComponent2(Match, {
|
|
6000
6221
|
get when() {
|
|
@@ -6002,15 +6223,15 @@ function ReviewFooter(props) {
|
|
|
6002
6223
|
},
|
|
6003
6224
|
get children() {
|
|
6004
6225
|
var _el$0 = _$createElement("text");
|
|
6005
|
-
_$
|
|
6226
|
+
_$insert(_el$0, () => uiText.autoApproval.unavailable);
|
|
6006
6227
|
_$effect((_$p) => _$setProp(_el$0, "fg", props.api.theme.current.warning, _$p));
|
|
6007
6228
|
return _el$0;
|
|
6008
6229
|
}
|
|
6009
6230
|
})];
|
|
6010
6231
|
}
|
|
6011
6232
|
}));
|
|
6012
|
-
_$effect((_$p) => _$setProp(_el$
|
|
6013
|
-
return _el$
|
|
6233
|
+
_$effect((_$p) => _$setProp(_el$7, "borderColor", props.api.theme.current.borderSubtle, _$p));
|
|
6234
|
+
return _el$7;
|
|
6014
6235
|
}
|
|
6015
6236
|
});
|
|
6016
6237
|
}
|
|
@@ -6090,7 +6311,7 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6090
6311
|
prompts = await withDeadline(api.lifecycle.signal, parsed.timeoutMs, (signal) => loadPrompts(parsed.instructions, signal), "Prompt loading");
|
|
6091
6312
|
config = parsed;
|
|
6092
6313
|
} catch (error) {
|
|
6093
|
-
configError = error instanceof Error ? error.message :
|
|
6314
|
+
configError = error instanceof Error ? error.message : uiText.review.invalidConfiguration;
|
|
6094
6315
|
}
|
|
6095
6316
|
api.lifecycle.signal.throwIfAborted();
|
|
6096
6317
|
const lifetime = lifetimeTracker(api);
|
|
@@ -6106,7 +6327,7 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6106
6327
|
try {
|
|
6107
6328
|
const click = (id) => notifications?.click(id);
|
|
6108
6329
|
const backend = notificationBackend ? notificationBackend(click, notificationConfig) : new LinuxNotifications(notificationConfig, click);
|
|
6109
|
-
notifications = new NotificationHost(api, new NotificationPolicy(notificationConfig, reviewOptions?.autoApprove === true,
|
|
6330
|
+
notifications = new NotificationHost(api, new NotificationPolicy(notificationConfig, reviewOptions?.autoApprove === true, backend), (id, signal) => modes.root(id, signal));
|
|
6110
6331
|
} catch {
|
|
6111
6332
|
}
|
|
6112
6333
|
}
|
|
@@ -6147,7 +6368,7 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6147
6368
|
once: (request, signal) => measured(traces?.get(request), "approval-reply", () => approval.once(request, signal))
|
|
6148
6369
|
} : approval,
|
|
6149
6370
|
visibleID: () => visibleApproval()
|
|
6150
|
-
}, void 0, modes, (fact) => notifications?.fact(fact));
|
|
6371
|
+
}, void 0, modes, (fact) => notifications?.fact(fact), lifetime.recordRating);
|
|
6151
6372
|
api.event.on("permission.asked", (event) => controller.asked(event.properties));
|
|
6152
6373
|
api.event.on("permission.replied", (event) => controller.replied(event.properties.requestID));
|
|
6153
6374
|
api.event.on("session.deleted", (event) => {
|
|
@@ -6265,45 +6486,48 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6265
6486
|
// The app slot lets this cover its title, sections, and footer while
|
|
6266
6487
|
// the original sidebar remains mounted beneath it.
|
|
6267
6488
|
(() => {
|
|
6268
|
-
var _el$
|
|
6269
|
-
_$insertNode(_el$
|
|
6270
|
-
_$insertNode(_el$
|
|
6271
|
-
_$insertNode(_el$
|
|
6489
|
+
var _el$1 = _$createElement("box"), _el$10 = _$createElement("text"), _el$11 = _$createElement("b"), _el$12 = _$createElement("box"), _el$17 = _$createElement("scrollbox");
|
|
6490
|
+
_$insertNode(_el$1, _el$10);
|
|
6491
|
+
_$insertNode(_el$1, _el$12);
|
|
6492
|
+
_$insertNode(_el$1, _el$17);
|
|
6272
6493
|
_$use((value) => {
|
|
6273
6494
|
panel = value;
|
|
6274
|
-
}, _el$
|
|
6275
|
-
_$setProp(_el$
|
|
6495
|
+
}, _el$1);
|
|
6496
|
+
_$setProp(_el$1, "renderAfter", () => {
|
|
6276
6497
|
const assessment = view().assessment;
|
|
6277
6498
|
if (assessment && paintedAssessment !== assessment && description && !description.isDestroyed && !description.streaming && description.content === displayText(assessment.desc) && description.getChildrenCount() && highlightingComplete(description)) paintedAssessment = assessment;
|
|
6278
6499
|
});
|
|
6279
|
-
_$setProp(_el$
|
|
6280
|
-
_$setProp(_el$
|
|
6281
|
-
_$setProp(_el$
|
|
6282
|
-
_$setProp(_el$
|
|
6283
|
-
_$setProp(_el$
|
|
6284
|
-
_$setProp(_el$
|
|
6285
|
-
_$setProp(_el$
|
|
6286
|
-
_$setProp(_el$
|
|
6287
|
-
_$setProp(_el$
|
|
6288
|
-
_$setProp(_el$
|
|
6289
|
-
_$insertNode(_el$
|
|
6290
|
-
_$setProp(_el$
|
|
6291
|
-
_$
|
|
6292
|
-
_$setProp(_el$
|
|
6293
|
-
_$setProp(_el$
|
|
6294
|
-
_$insert(_el$
|
|
6500
|
+
_$setProp(_el$1, "position", "absolute");
|
|
6501
|
+
_$setProp(_el$1, "top", 0);
|
|
6502
|
+
_$setProp(_el$1, "right", 0);
|
|
6503
|
+
_$setProp(_el$1, "bottom", 0);
|
|
6504
|
+
_$setProp(_el$1, "width", 42);
|
|
6505
|
+
_$setProp(_el$1, "zIndex", 1);
|
|
6506
|
+
_$setProp(_el$1, "paddingTop", 1);
|
|
6507
|
+
_$setProp(_el$1, "paddingBottom", 1);
|
|
6508
|
+
_$setProp(_el$1, "paddingLeft", 2);
|
|
6509
|
+
_$setProp(_el$1, "paddingRight", 2);
|
|
6510
|
+
_$insertNode(_el$10, _el$11);
|
|
6511
|
+
_$setProp(_el$10, "flexShrink", 0);
|
|
6512
|
+
_$insert(_el$11, () => uiText.review.heading);
|
|
6513
|
+
_$setProp(_el$12, "marginTop", 1);
|
|
6514
|
+
_$setProp(_el$12, "flexShrink", 0);
|
|
6515
|
+
_$insert(_el$12, _$createComponent2(Show, {
|
|
6295
6516
|
get when() {
|
|
6296
6517
|
return rating() !== void 0;
|
|
6297
6518
|
},
|
|
6298
6519
|
get children() {
|
|
6299
|
-
var _el$
|
|
6300
|
-
_$insertNode(_el$
|
|
6301
|
-
_$insert(_el$
|
|
6302
|
-
|
|
6303
|
-
|
|
6520
|
+
var _el$13 = _$createElement("text"), _el$14 = _$createElement("b");
|
|
6521
|
+
_$insertNode(_el$13, _el$14);
|
|
6522
|
+
_$insert(_el$14, (() => {
|
|
6523
|
+
var _c$2 = _$memo2(() => !!rating());
|
|
6524
|
+
return () => _c$2() ? uiText.review.safe : uiText.review.unsafe;
|
|
6525
|
+
})());
|
|
6526
|
+
_$effect((_$p) => _$setProp(_el$13, "fg", rating() ? api.theme.current.success : api.theme.current.error, _$p));
|
|
6527
|
+
return _el$13;
|
|
6304
6528
|
}
|
|
6305
6529
|
}), null);
|
|
6306
|
-
_$insert(_el$
|
|
6530
|
+
_$insert(_el$12, _$createComponent2(Show, {
|
|
6307
6531
|
get when() {
|
|
6308
6532
|
return _$memo2(() => view().status === "analyzing")() && rating() === void 0;
|
|
6309
6533
|
},
|
|
@@ -6316,28 +6540,28 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6316
6540
|
});
|
|
6317
6541
|
}
|
|
6318
6542
|
}), null);
|
|
6319
|
-
_$insert(_el$
|
|
6543
|
+
_$insert(_el$12, _$createComponent2(Show, {
|
|
6320
6544
|
get when() {
|
|
6321
6545
|
return view().status === "unavailable";
|
|
6322
6546
|
},
|
|
6323
6547
|
get children() {
|
|
6324
|
-
var _el$
|
|
6325
|
-
_$insertNode(_el$
|
|
6326
|
-
_$
|
|
6327
|
-
_$effect((_$p) => _$setProp(_el$
|
|
6328
|
-
return _el$
|
|
6548
|
+
var _el$15 = _$createElement("text"), _el$16 = _$createElement("b");
|
|
6549
|
+
_$insertNode(_el$15, _el$16);
|
|
6550
|
+
_$insert(_el$16, () => uiText.review.unavailable);
|
|
6551
|
+
_$effect((_$p) => _$setProp(_el$15, "fg", api.theme.current.warning, _$p));
|
|
6552
|
+
return _el$15;
|
|
6329
6553
|
}
|
|
6330
6554
|
}), null);
|
|
6331
6555
|
_$use((value) => {
|
|
6332
6556
|
scroll = value;
|
|
6333
|
-
}, _el$
|
|
6334
|
-
_$setProp(_el$
|
|
6335
|
-
_$setProp(_el$
|
|
6336
|
-
_$setProp(_el$
|
|
6337
|
-
_$setProp(_el$
|
|
6557
|
+
}, _el$17);
|
|
6558
|
+
_$setProp(_el$17, "marginTop", 1);
|
|
6559
|
+
_$setProp(_el$17, "flexGrow", 1);
|
|
6560
|
+
_$setProp(_el$17, "minHeight", 0);
|
|
6561
|
+
_$setProp(_el$17, "contentOptions", {
|
|
6338
6562
|
minHeight: 0
|
|
6339
6563
|
});
|
|
6340
|
-
_$insert(_el$
|
|
6564
|
+
_$insert(_el$17, _$createComponent2(ReviewDescription, {
|
|
6341
6565
|
api,
|
|
6342
6566
|
get text() {
|
|
6343
6567
|
return report();
|
|
@@ -6349,46 +6573,46 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6349
6573
|
description = value;
|
|
6350
6574
|
}
|
|
6351
6575
|
}), null);
|
|
6352
|
-
_$insert(_el$
|
|
6576
|
+
_$insert(_el$17, _$createComponent2(Show, {
|
|
6353
6577
|
get when() {
|
|
6354
6578
|
return view().status === "unavailable";
|
|
6355
6579
|
},
|
|
6356
6580
|
get children() {
|
|
6357
|
-
var _el$
|
|
6358
|
-
_$setProp(_el$
|
|
6359
|
-
_$setProp(_el$
|
|
6360
|
-
_$insert(_el$
|
|
6361
|
-
_$effect((_$p) => _$setProp(_el$
|
|
6362
|
-
return _el$
|
|
6581
|
+
var _el$18 = _$createElement("text");
|
|
6582
|
+
_$setProp(_el$18, "width", "100%");
|
|
6583
|
+
_$setProp(_el$18, "flexShrink", 0);
|
|
6584
|
+
_$insert(_el$18, () => displayText(view().error ?? uiText.review.failed));
|
|
6585
|
+
_$effect((_$p) => _$setProp(_el$18, "fg", api.theme.current.text, _$p));
|
|
6586
|
+
return _el$18;
|
|
6363
6587
|
}
|
|
6364
6588
|
}), null);
|
|
6365
|
-
_$insert(_el$
|
|
6589
|
+
_$insert(_el$17, _$createComponent2(Show, {
|
|
6366
6590
|
get when() {
|
|
6367
6591
|
return view().assessment?.usage;
|
|
6368
6592
|
},
|
|
6369
6593
|
children: (usage) => [(() => {
|
|
6370
|
-
var _el$
|
|
6371
|
-
_$setProp(_el$
|
|
6372
|
-
_$setProp(_el$
|
|
6373
|
-
_$setProp(_el$
|
|
6374
|
-
_$insert(_el$
|
|
6375
|
-
_$effect((_$p) => _$setProp(_el$
|
|
6376
|
-
return _el$
|
|
6594
|
+
var _el$19 = _$createElement("text");
|
|
6595
|
+
_$setProp(_el$19, "marginTop", 1);
|
|
6596
|
+
_$setProp(_el$19, "width", "100%");
|
|
6597
|
+
_$setProp(_el$19, "flexShrink", 0);
|
|
6598
|
+
_$insert(_el$19, () => usageText(usage()));
|
|
6599
|
+
_$effect((_$p) => _$setProp(_el$19, "fg", api.theme.current.textMuted, _$p));
|
|
6600
|
+
return _el$19;
|
|
6377
6601
|
})(), _$createComponent2(Show, {
|
|
6378
6602
|
get when() {
|
|
6379
6603
|
return lifetime.text();
|
|
6380
6604
|
},
|
|
6381
6605
|
children: (text) => (() => {
|
|
6382
|
-
var _el$
|
|
6383
|
-
_$setProp(_el$
|
|
6384
|
-
_$setProp(_el$
|
|
6385
|
-
_$insert(_el$
|
|
6386
|
-
_$effect((_$p) => _$setProp(_el$
|
|
6387
|
-
return _el$
|
|
6606
|
+
var _el$20 = _$createElement("text");
|
|
6607
|
+
_$setProp(_el$20, "width", "100%");
|
|
6608
|
+
_$setProp(_el$20, "flexShrink", 0);
|
|
6609
|
+
_$insert(_el$20, text);
|
|
6610
|
+
_$effect((_$p) => _$setProp(_el$20, "fg", api.theme.current.textMuted, _$p));
|
|
6611
|
+
return _el$20;
|
|
6388
6612
|
})()
|
|
6389
6613
|
})]
|
|
6390
6614
|
}), null);
|
|
6391
|
-
_$insert(_el$
|
|
6615
|
+
_$insert(_el$1, _$createComponent2(ReviewFooter, {
|
|
6392
6616
|
api,
|
|
6393
6617
|
get view() {
|
|
6394
6618
|
return view();
|
|
@@ -6405,16 +6629,16 @@ async function reviewTui(api, options, fileIO, observer, notificationBackend) {
|
|
|
6405
6629
|
foregroundColor: api.theme.current.textMuted
|
|
6406
6630
|
}
|
|
6407
6631
|
};
|
|
6408
|
-
_v$7 !== _p$.e && (_p$.e = _$setProp(_el$
|
|
6409
|
-
_v$8 !== _p$.t && (_p$.t = _$setProp(_el$
|
|
6410
|
-
_v$9 !== _p$.a && (_p$.a = _$setProp(_el$
|
|
6632
|
+
_v$7 !== _p$.e && (_p$.e = _$setProp(_el$1, "backgroundColor", _v$7, _p$.e));
|
|
6633
|
+
_v$8 !== _p$.t && (_p$.t = _$setProp(_el$10, "fg", _v$8, _p$.t));
|
|
6634
|
+
_v$9 !== _p$.a && (_p$.a = _$setProp(_el$17, "scrollbarOptions", _v$9, _p$.a));
|
|
6411
6635
|
return _p$;
|
|
6412
6636
|
}, {
|
|
6413
6637
|
e: void 0,
|
|
6414
6638
|
t: void 0,
|
|
6415
6639
|
a: void 0
|
|
6416
6640
|
});
|
|
6417
|
-
return _el$
|
|
6641
|
+
return _el$1;
|
|
6418
6642
|
})()
|
|
6419
6643
|
);
|
|
6420
6644
|
}
|