@dan-ai-studio/dshopencodego 0.1.76 → 0.1.78
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.en.md +4 -4
- package/README.md +4 -4
- package/lib/client.js +69 -32
- package/lib/index.js +61 -25
- package/lib/types/config.d.ts +4 -4
- package/lib/types/request-log/store.d.ts +27 -13
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -113,7 +113,7 @@ Configuration lives in the profile's `cordis.patch.yml`:
|
|
|
113
113
|
some-new-model: openai-responses # the only legal values are anthropic-messages / openai-completions / openai-responses
|
|
114
114
|
pinnedAddresses: # fixed IP: host -> address (only the gateway host counts)
|
|
115
115
|
opencode.ai: 104.18.32.47 # IPv4 or IPv6; an invalid or absent address uses the system DNS
|
|
116
|
-
requestLogEnabled:
|
|
116
|
+
requestLogEnabled: false # request-log master switch, default off: on registers the sidebar entry and the conversation tab; off leaves no entry and reads byte-for-byte as without the log
|
|
117
117
|
requestLogDebug: false # debug detail: pool events and the request user agent; session ids are always whole
|
|
118
118
|
requestLogMaxEntries: 200 # most entries to keep, 50 to 500; oldest are dropped first
|
|
119
119
|
retryPolicy: # optional: this route's retry policy, executed by dsh-llm-retry
|
|
@@ -139,7 +139,7 @@ The API key comes from the Harness credential store (reference name `OPENCODE_GO
|
|
|
139
139
|
- The settings page shows and accepts the value in **seconds** (a number of milliseconds is not readable at a glance) and stores it in milliseconds. Range: 1 to 1800000 milliseconds.
|
|
140
140
|
- Once a connection has been idle that long, the pool **reclaims it** on its own — no separate mechanism is needed. The actual release comes a couple of seconds later than the setting, which is the client's own small margin beyond the deadline the server states. When the plugin is unloaded, the pool is **closed with it**: the pool belongs to the plugin and should not be left behind on the machine.
|
|
141
141
|
|
|
142
|
-
**Request log**: per-request records,
|
|
142
|
+
**Request log**: per-request records, off by default; turning them on registers the sidebar entry and the conversation tab, and turning them off removes those entries and returns the request path to exactly as without the log. The settings page's "Request log" group holds the master switch, the debug switch (pool events and the request user agent, default off), and the retention count (default 200).
|
|
143
143
|
|
|
144
144
|
- **Two entries, one page**:
|
|
145
145
|
- **A sidebar icon → a full page in the main column**: every request across sessions. The log is a process-wide thing, so the sidebar is where it belongs — a fixed, always-visible spot, and the same pattern the Harness's own plugin manager uses.
|
|
@@ -151,8 +151,8 @@ The API key comes from the Harness credential store (reference name `OPENCODE_GO
|
|
|
151
151
|
- **The In column is uncached new input**: the gateway reports a prompt as "uncached plus cache hits" and the log records only the new part — a fully prefix-cached turn reads as a tiny input with a 100% hit, which is not a bug. Hover a column head for its definition.
|
|
152
152
|
- **Content split**: below the body size the detail shows **Text X / Images Y** — images estimated as base64 per deduped attachment (each capped at the route's encoded target), text as the residual, so the two always sum to the total. Image-dominated turns read at a glance.
|
|
153
153
|
- Session ids are **always stored whole**: an id is a conversation identity, not a credential, and a truncated one reads two conversations as one.
|
|
154
|
-
- The log is a ring buffer (oldest dropped first) that dies with the process; snapshots are read on
|
|
155
|
-
- The credential never enters a record: the recorder takes no headers argument at all, only the allowlisted session id and user agent. Failed requests are recorded with their error code; a
|
|
154
|
+
- The log is a ring buffer (oldest dropped first) that dies with the process; snapshots are read on open and on manual refresh, plus once a second while any request is running, so a page nobody opens costs no traffic.
|
|
155
|
+
- The credential never enters a record: the recorder takes no headers argument at all, only the allowlisted session id and user agent. A request gets a "Requesting" placeholder row the moment it starts (the status column counts its seconds) and the row is completed in place when it ends. Failed requests are recorded with their error code; a stream the consumer abandoned early is recorded as `ABORTED` instead of vanishing silently.
|
|
156
156
|
|
|
157
157
|
**Images**: an over-budget image is refused explicitly rather than silently downscaled or dropped. The budgets ride one preset `imageBudgetPreset` (three choices, default `dsh`), not three numbers:
|
|
158
158
|
|
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ DSH 桌面端是 Electron 壳 + 完整的 dsh Web 应用,本插件对它同样
|
|
|
112
112
|
some-new-model: openai-responses # 合法值只有 anthropic-messages / openai-completions / openai-responses
|
|
113
113
|
pinnedAddresses: # 固定 IP:域名 → 地址(只对网关域名生效)
|
|
114
114
|
opencode.ai: 104.18.32.47 # 可写 IPv4 或 IPv6;不合法或未设置时走系统 DNS
|
|
115
|
-
requestLogEnabled:
|
|
115
|
+
requestLogEnabled: false # 请求日志总开关,默认关闭:打开后注册侧边栏入口与会话标签页;关闭后无入口,请求路径与没有该日志逐字节一致
|
|
116
116
|
requestLogDebug: false # 排错详情:连接池事件与请求 UA;会话 id 始终完整记录
|
|
117
117
|
requestLogMaxEntries: 200 # 最多保留条数,50 ~ 500,超限丢弃最旧的
|
|
118
118
|
retryPolicy: # 可选:本路由的重试策略(由 dsh-llm-retry 执行)
|
|
@@ -138,7 +138,7 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
138
138
|
- 设置页里以**秒**显示与填写(毫秒的数字人读不出量级),存储时换算成毫秒。取值 1 ~ 1800000 毫秒。
|
|
139
139
|
- 空闲满这个时间之后,连接由连接池**自己回收**,不需要额外的清理机制(实际释放时间比设置值多约 2 秒,那是客户端在服务端给的期限之外自留的一点缓冲)。插件被卸载时,连接池也会**一并关掉**——它是插件的,不该留在机器上。
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
**请求日志**:默认关闭的逐请求记录,打开它才注册侧边栏入口与会话标签页;关掉它入口一并消失,请求路径与没有该日志逐字节一致。设置页"请求日志"分组里有总开关、排错详情开关(连接池事件与请求 UA,默认关闭)、保留条数(默认 200)。
|
|
142
142
|
|
|
143
143
|
- **两个入口,同一个页面**:
|
|
144
144
|
- **侧边栏图标 → 主区域整页**:跨会话看全部请求。日志是进程级的数据,放在侧边栏最合理——位置固定、常驻可见,和 DSH 自己的插件管理器同一个范式。
|
|
@@ -150,8 +150,8 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
|
|
|
150
150
|
- **输入列是未缓存的新增 token**:网关把整段 prompt 拆成"未缓存+缓存命中"两部分上报,输入列只记新增的——同一会话前缀缓存全中时输入很小、缓存命中 100%,不是 bug。悬停列头有口径说明。
|
|
151
151
|
- **内容拆分**:明细里"请求体"下面跟一行"文本 X /图片 Y"——图片是各去重后附件按 base64 估的占比(单张按路由的编码上限封顶),文本是总数减图片的剩余,两者恒等于请求体总数。图片 dominant 的请求一眼就能看出来。
|
|
152
152
|
- 会话 id **始终完整记录**(它是会话身份,不是凭据;截断后两个会话会读成一个)。
|
|
153
|
-
-
|
|
154
|
-
- 凭据永远不进记录:记录器根本不接收请求头参数,只收白名单里的会话 id 与 UA
|
|
153
|
+
- 记录的是环形缓冲(超限丢最旧),进程重启即清;页面平时只在打开与手动刷新时读快照,有请求中时每秒自动刷新一次直到结束,没人打开的页面零流量。
|
|
154
|
+
- 凭据永远不进记录:记录器根本不接收请求头参数,只收白名单里的会话 id 与 UA;请求一开始就有一条"请求中"占位行(状态列读秒),结束时原位补全为完整记录。失败的请求同样记一条(含错误码);消费者提前终止的流记一条 `ABORTED`,不再无声丢失。
|
|
155
155
|
|
|
156
156
|
**图片**:三个字段各管一层,超限的图片会被明确拒绝,而不是静默降质或丢弃:
|
|
157
157
|
|
package/lib/client.js
CHANGED
|
@@ -258,6 +258,9 @@ function parseEntry(value) {
|
|
|
258
258
|
...row["decodeMs"] === void 0 ? {} : { decodeMs: count("decodeMs", row) },
|
|
259
259
|
...row["streamMs"] === void 0 ? {} : { streamMs: count("streamMs", row) },
|
|
260
260
|
...row["tps"] === void 0 ? {} : { tps: rate("tps", row) },
|
|
261
|
+
// Running rows cross the wire too: a viewer opened mid-request must
|
|
262
|
+
// see them. Older snapshots predate the flag and read as finished.
|
|
263
|
+
...row["running"] === true ? { running: true } : {},
|
|
261
264
|
ok: row["ok"] === true,
|
|
262
265
|
...typeof row["code"] === "string" ? { code: row["code"] } : {},
|
|
263
266
|
...typeof row["poolKey"] === "string" ? { poolKey: row["poolKey"] } : {},
|
|
@@ -513,7 +516,7 @@ var en = {
|
|
|
513
516
|
imageBudgetPresetDshopencodego: "This plugin (1.44M px / 400KB / 6MB)",
|
|
514
517
|
imageBudgetPresetPiAi: "pi-ai catalog (2000\xD72000 / 4.5MB / 20MB)",
|
|
515
518
|
requestLogTitle: "Request log",
|
|
516
|
-
requestLogHint: "Per-request records for troubleshooting.
|
|
519
|
+
requestLogHint: "Per-request records for troubleshooting. Off by default; turning it on registers the sidebar entry and the conversation tab, and the request path stays exactly as without the log when off.",
|
|
517
520
|
requestLogPageHint: "One row per request. Click a column head to sort; the table scrolls and the filters stay put.",
|
|
518
521
|
requestLogEnabled: "Record requests",
|
|
519
522
|
requestLogDebug: "Debug detail",
|
|
@@ -525,6 +528,7 @@ var en = {
|
|
|
525
528
|
requestLogOpenSessionFailed: "That session is archived and cannot be opened.",
|
|
526
529
|
requestLogClose: "Close",
|
|
527
530
|
requestLogRefresh: "Refresh",
|
|
531
|
+
requestLogRunning: "Requesting",
|
|
528
532
|
requestLogEmpty: "No requests recorded. Turn the log on, then make a request.",
|
|
529
533
|
requestLogDisabled: "The request log is off.",
|
|
530
534
|
requestLogFilterModel: "Model",
|
|
@@ -711,7 +715,7 @@ var zh = {
|
|
|
711
715
|
imageBudgetPresetDshopencodego: "\u672C\u63D2\u4EF6\uFF08144\u4E07\u50CF\u7D20 / 400KB / 6MB\uFF09",
|
|
712
716
|
imageBudgetPresetPiAi: "pi-ai \u76EE\u5F55\uFF082000\xD72000 / 4.5MB / 20MB\uFF09",
|
|
713
717
|
requestLogTitle: "\u8BF7\u6C42\u65E5\u5FD7",
|
|
714
|
-
requestLogHint: "\u6392\u67E5\u7528\u7684\u9010\u8BF7\u6C42\u8BB0\u5F55\u3002\u9ED8\u8BA4\
|
|
718
|
+
requestLogHint: "\u6392\u67E5\u7528\u7684\u9010\u8BF7\u6C42\u8BB0\u5F55\u3002\u9ED8\u8BA4\u5173\u95ED\uFF1B\u6253\u5F00\u540E\u6CE8\u518C\u4FA7\u8FB9\u680F\u5165\u53E3\u4E0E\u4F1A\u8BDD\u6807\u7B7E\u9875\uFF0C\u5173\u95ED\u540E\u8BF7\u6C42\u8DEF\u5F84\u4E0E\u6CA1\u6709\u8BE5\u65E5\u5FD7\u9010\u5B57\u8282\u4E00\u81F4\u3002",
|
|
715
719
|
requestLogPageHint: "\u6BCF\u4E2A\u8BF7\u6C42\u4E00\u884C\u3002\u70B9\u5217\u5934\u53EF\u6392\u5E8F\uFF1B\u8868\u683C\u6EDA\u52A8\uFF0C\u7B5B\u9009\u680F\u56FA\u5B9A\u5728\u4E0A\u65B9\u3002",
|
|
716
720
|
requestLogEnabled: "\u8BB0\u5F55\u8BF7\u6C42",
|
|
717
721
|
requestLogDebug: "\u6392\u9519\u8BE6\u60C5",
|
|
@@ -723,6 +727,7 @@ var zh = {
|
|
|
723
727
|
requestLogOpenSessionFailed: "\u8BE5\u4F1A\u8BDD\u5DF2\u5F52\u6863\uFF0C\u6253\u4E0D\u5F00\u3002",
|
|
724
728
|
requestLogClose: "\u5173\u95ED",
|
|
725
729
|
requestLogRefresh: "\u5237\u65B0",
|
|
730
|
+
requestLogRunning: "\u8BF7\u6C42\u4E2D",
|
|
726
731
|
requestLogEmpty: "\u6682\u65E0\u8BB0\u5F55\u3002\u5148\u6253\u5F00\u65E5\u5FD7\u5F00\u5173\uFF0C\u518D\u53D1\u8D77\u4E00\u6B21\u8BF7\u6C42\u3002",
|
|
727
732
|
requestLogDisabled: "\u8BF7\u6C42\u65E5\u5FD7\u672A\u5F00\u542F\u3002",
|
|
728
733
|
requestLogFilterModel: "\u6A21\u578B",
|
|
@@ -1066,7 +1071,7 @@ function settingsOf(value) {
|
|
|
1066
1071
|
pinnedAddresses: Object.fromEntries(pinEntries),
|
|
1067
1072
|
keepAliveTimeoutMs: typeof keepAlive === "number" ? keepAlive : DEFAULT_KEEP_ALIVE_TIMEOUT_MS,
|
|
1068
1073
|
imageBudgetPreset: isImageBudgetPreset(imageBudgetPreset) ? imageBudgetPreset : "dsh",
|
|
1069
|
-
requestLogEnabled: typeof requestLogEnabled === "boolean" ? requestLogEnabled :
|
|
1074
|
+
requestLogEnabled: typeof requestLogEnabled === "boolean" ? requestLogEnabled : false,
|
|
1070
1075
|
requestLogDebug: typeof requestLogDebug === "boolean" ? requestLogDebug : false,
|
|
1071
1076
|
requestLogMaxEntries: typeof requestLogMaxEntries === "number" ? requestLogMaxEntries : DEFAULT_REQUEST_LOG_MAX_ENTRIES
|
|
1072
1077
|
};
|
|
@@ -1965,7 +1970,7 @@ function Section({ controller, t, getLocale, openRequestLog }) {
|
|
|
1965
1970
|
"input",
|
|
1966
1971
|
{
|
|
1967
1972
|
type: "checkbox",
|
|
1968
|
-
checked: state.settings?.requestLogEnabled ??
|
|
1973
|
+
checked: state.settings?.requestLogEnabled ?? false,
|
|
1969
1974
|
disabled: !editable || state.saving,
|
|
1970
1975
|
onChange: (event) => {
|
|
1971
1976
|
void act(async () => {
|
|
@@ -2016,7 +2021,7 @@ function Section({ controller, t, getLocale, openRequestLog }) {
|
|
|
2016
2021
|
),
|
|
2017
2022
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: t("requestLogMaxEntriesHint") })
|
|
2018
2023
|
] }),
|
|
2019
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.row, children: [
|
|
2024
|
+
state.settings?.requestLogEnabled === true && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.row, children: [
|
|
2020
2025
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: section_default.button, onClick: () => {
|
|
2021
2026
|
setLogOpenFailed(!(openRequestLog?.() ?? false));
|
|
2022
2027
|
}, children: t("requestLogOpen") }),
|
|
@@ -2140,10 +2145,10 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
|
|
|
2140
2145
|
const style = document.createElement("style");
|
|
2141
2146
|
style.dataset.plugin = "@dan-ai-studio/dshopencodego";
|
|
2142
2147
|
style.dataset.pluginCss = id2;
|
|
2143
|
-
style.textContent = ".Lx7MOq_page{box-sizing:border-box;background:var(--dsw-alias-bg-base);height:100%;min-height:0;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}.Lx7MOq_pageLoose{height:auto}.Lx7MOq_pageLoose .Lx7MOq_tableWrap{overflow:visible}.Lx7MOq_pageLoose .Lx7MOq_stuck{z-index:3;background:var(--dsw-alias-bg-base);position:sticky;top:0}.Lx7MOq_pageLoose .Lx7MOq_table th{top:var(--request-log-stuck,0px)}.Lx7MOq_pageLoose .Lx7MOq_foot{z-index:2;background:var(--dsw-alias-bg-base);position:sticky;bottom:0}.Lx7MOq_head{flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:20px clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_headText{min-width:0}.Lx7MOq_title{margin:0;font-size:20px;font-weight:500;line-height:28px}.Lx7MOq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.Lx7MOq_headActions{flex:none;align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_button{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.Lx7MOq_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_button:disabled{cursor:default;opacity:.5}.Lx7MOq_summary{flex-wrap:wrap;flex:none;gap:8px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_card{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex-direction:column;gap:2px;min-width:104px;padding:8px 12px;display:flex}.Lx7MOq_cardLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.Lx7MOq_cardValue{font-variant-numeric:tabular-nums;font-size:16px;font-weight:500;line-height:22px}.Lx7MOq_cardError .Lx7MOq_cardValue{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_cardWarn .Lx7MOq_cardValue{color:var(--dsw-alias-state-warn-primary)}.Lx7MOq_toolbar{flex-wrap:wrap;flex:none;align-items:center;gap:8px 12px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_field{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.Lx7MOq_select,.Lx7MOq_input{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;padding:0 8px;font-size:13px}.Lx7MOq_input{width:160px}.Lx7MOq_inputNumber{width:72px}.Lx7MOq_checkbox{accent-color:var(--dsw-alias-brand-primary)}.Lx7MOq_sessionLink{color:var(--dsw-alias-link);font:inherit;text-align:left;cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.Lx7MOq_sessionLink:hover{text-decoration:underline}.Lx7MOq_tableWrap{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex:1;min-height:0;margin:0 clamp(16px,3vw,32px);overflow:auto}.Lx7MOq_table{border-collapse:separate;border-spacing:0;width:100%;min-width:940px;font-size:13px;line-height:20px}.Lx7MOq_table th{z-index:1;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);text-align:left;white-space:nowrap;padding:8px 10px;font-size:12px;font-weight:500;position:sticky;top:0}.Lx7MOq_thSortable{cursor:pointer;user-select:none}.Lx7MOq_thSortable:hover{color:var(--dsw-alias-label-primary)}.Lx7MOq_table td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;font-variant-numeric:tabular-nums;padding:6px 10px}.Lx7MOq_table tbody tr:last-child td{border-bottom:none}.Lx7MOq_row{cursor:pointer}.Lx7MOq_row:hover td{background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_rowOk td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-border-l3)}.Lx7MOq_rowSlow td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-warn-primary)}.Lx7MOq_rowFail td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-error-primary)}.Lx7MOq_rowFail td:last-child,.Lx7MOq_rowSlow td:last-child{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_group td{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);padding-top:12px;font-weight:500}.Lx7MOq_detail td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:normal;background:var(--dsw-alias-bg-layer-2);padding:12px 10px 16px}.Lx7MOq_detailGrid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:6px 24px;display:grid}.Lx7MOq_detailItem{gap:8px;min-width:0;font-size:13px;line-height:20px;display:flex}.Lx7MOq_detailLabel{color:var(--dsw-alias-label-tertiary);flex:none}.Lx7MOq_detailValue{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-primary)}.Lx7MOq_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.Lx7MOq_empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:48px 16px}.Lx7MOq_foot{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:12px clamp(16px,3vw,32px) 20px;font-size:12px;display:flex}.Lx7MOq_pager{align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_notice{color:var(--dsw-alias-label-secondary);padding:0 clamp(16px,3vw,32px) 12px;font-size:13px;line-height:20px}.Lx7MOq_noticeError{color:var(--dsw-alias-state-error-primary)}";
|
|
2148
|
+
style.textContent = ".Lx7MOq_page{box-sizing:border-box;background:var(--dsw-alias-bg-base);height:100%;min-height:0;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}.Lx7MOq_pageLoose{height:auto}.Lx7MOq_pageLoose .Lx7MOq_tableWrap{overflow:visible}.Lx7MOq_pageLoose .Lx7MOq_stuck{z-index:3;background:var(--dsw-alias-bg-base);position:sticky;top:0}.Lx7MOq_pageLoose .Lx7MOq_table th{top:var(--request-log-stuck,0px)}.Lx7MOq_pageLoose .Lx7MOq_foot{z-index:2;background:var(--dsw-alias-bg-base);position:sticky;bottom:0}.Lx7MOq_head{flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:20px clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_headText{min-width:0}.Lx7MOq_title{margin:0;font-size:20px;font-weight:500;line-height:28px}.Lx7MOq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.Lx7MOq_headActions{flex:none;align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_button{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.Lx7MOq_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_button:disabled{cursor:default;opacity:.5}.Lx7MOq_summary{flex-wrap:wrap;flex:none;gap:8px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_card{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex-direction:column;gap:2px;min-width:104px;padding:8px 12px;display:flex}.Lx7MOq_cardLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.Lx7MOq_cardValue{font-variant-numeric:tabular-nums;font-size:16px;font-weight:500;line-height:22px}.Lx7MOq_cardError .Lx7MOq_cardValue{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_cardWarn .Lx7MOq_cardValue{color:var(--dsw-alias-state-warn-primary)}.Lx7MOq_toolbar{flex-wrap:wrap;flex:none;align-items:center;gap:8px 12px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_field{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.Lx7MOq_select,.Lx7MOq_input{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;padding:0 8px;font-size:13px}.Lx7MOq_input{width:160px}.Lx7MOq_inputNumber{width:72px}.Lx7MOq_checkbox{accent-color:var(--dsw-alias-brand-primary)}.Lx7MOq_sessionLink{color:var(--dsw-alias-link);font:inherit;text-align:left;cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.Lx7MOq_sessionLink:hover{text-decoration:underline}.Lx7MOq_tableWrap{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex:1;min-height:0;margin:0 clamp(16px,3vw,32px);overflow:auto}.Lx7MOq_table{border-collapse:separate;border-spacing:0;width:100%;min-width:940px;font-size:13px;line-height:20px}.Lx7MOq_table th{z-index:1;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);text-align:left;white-space:nowrap;padding:8px 10px;font-size:12px;font-weight:500;position:sticky;top:0}.Lx7MOq_thSortable{cursor:pointer;user-select:none}.Lx7MOq_thSortable:hover{color:var(--dsw-alias-label-primary)}.Lx7MOq_table td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;font-variant-numeric:tabular-nums;padding:6px 10px}.Lx7MOq_table tbody tr:last-child td{border-bottom:none}.Lx7MOq_row{cursor:pointer}.Lx7MOq_row:hover td{background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_rowOk td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-border-l3)}.Lx7MOq_rowSlow td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-warn-primary)}.Lx7MOq_rowFail td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-error-primary)}.Lx7MOq_rowRunning td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary)}.Lx7MOq_rowFail td:last-child,.Lx7MOq_rowSlow td:last-child{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_group td{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);padding-top:12px;font-weight:500}.Lx7MOq_detail td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:normal;background:var(--dsw-alias-bg-layer-2);padding:12px 10px 16px}.Lx7MOq_detailGrid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:6px 24px;display:grid}.Lx7MOq_detailItem{gap:8px;min-width:0;font-size:13px;line-height:20px;display:flex}.Lx7MOq_detailLabel{color:var(--dsw-alias-label-tertiary);flex:none}.Lx7MOq_detailValue{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-primary)}.Lx7MOq_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.Lx7MOq_empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:48px 16px}.Lx7MOq_foot{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:12px clamp(16px,3vw,32px) 20px;font-size:12px;display:flex}.Lx7MOq_pager{align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_notice{color:var(--dsw-alias-label-secondary);padding:0 clamp(16px,3vw,32px) 12px;font-size:13px;line-height:20px}.Lx7MOq_noticeError{color:var(--dsw-alias-state-error-primary)}";
|
|
2144
2149
|
document.head.appendChild(style);
|
|
2145
2150
|
}
|
|
2146
|
-
var request_log_default = { "button": "Lx7MOq_button", "card": "Lx7MOq_card", "cardError": "Lx7MOq_cardError", "cardLabel": "Lx7MOq_cardLabel", "cardValue": "Lx7MOq_cardValue", "cardWarn": "Lx7MOq_cardWarn", "checkbox": "Lx7MOq_checkbox", "detail": "Lx7MOq_detail", "detailGrid": "Lx7MOq_detailGrid", "detailItem": "Lx7MOq_detailItem", "detailLabel": "Lx7MOq_detailLabel", "detailValue": "Lx7MOq_detailValue", "empty": "Lx7MOq_empty", "field": "Lx7MOq_field", "foot": "Lx7MOq_foot", "group": "Lx7MOq_group", "head": "Lx7MOq_head", "headActions": "Lx7MOq_headActions", "headText": "Lx7MOq_headText", "input": "Lx7MOq_input", "inputNumber": "Lx7MOq_inputNumber", "mono": "Lx7MOq_mono", "notice": "Lx7MOq_notice", "noticeError": "Lx7MOq_noticeError", "page": "Lx7MOq_page", "pageLoose": "Lx7MOq_pageLoose", "pager": "Lx7MOq_pager", "row": "Lx7MOq_row", "rowFail": "Lx7MOq_rowFail", "rowOk": "Lx7MOq_rowOk", "rowSlow": "Lx7MOq_rowSlow", "select": "Lx7MOq_select", "sessionLink": "Lx7MOq_sessionLink", "stuck": "Lx7MOq_stuck", "subtitle": "Lx7MOq_subtitle", "summary": "Lx7MOq_summary", "table": "Lx7MOq_table", "tableWrap": "Lx7MOq_tableWrap", "thSortable": "Lx7MOq_thSortable", "title": "Lx7MOq_title", "toolbar": "Lx7MOq_toolbar" };
|
|
2151
|
+
var request_log_default = { "button": "Lx7MOq_button", "card": "Lx7MOq_card", "cardError": "Lx7MOq_cardError", "cardLabel": "Lx7MOq_cardLabel", "cardValue": "Lx7MOq_cardValue", "cardWarn": "Lx7MOq_cardWarn", "checkbox": "Lx7MOq_checkbox", "detail": "Lx7MOq_detail", "detailGrid": "Lx7MOq_detailGrid", "detailItem": "Lx7MOq_detailItem", "detailLabel": "Lx7MOq_detailLabel", "detailValue": "Lx7MOq_detailValue", "empty": "Lx7MOq_empty", "field": "Lx7MOq_field", "foot": "Lx7MOq_foot", "group": "Lx7MOq_group", "head": "Lx7MOq_head", "headActions": "Lx7MOq_headActions", "headText": "Lx7MOq_headText", "input": "Lx7MOq_input", "inputNumber": "Lx7MOq_inputNumber", "mono": "Lx7MOq_mono", "notice": "Lx7MOq_notice", "noticeError": "Lx7MOq_noticeError", "page": "Lx7MOq_page", "pageLoose": "Lx7MOq_pageLoose", "pager": "Lx7MOq_pager", "row": "Lx7MOq_row", "rowFail": "Lx7MOq_rowFail", "rowOk": "Lx7MOq_rowOk", "rowRunning": "Lx7MOq_rowRunning", "rowSlow": "Lx7MOq_rowSlow", "select": "Lx7MOq_select", "sessionLink": "Lx7MOq_sessionLink", "stuck": "Lx7MOq_stuck", "subtitle": "Lx7MOq_subtitle", "summary": "Lx7MOq_summary", "table": "Lx7MOq_table", "tableWrap": "Lx7MOq_tableWrap", "thSortable": "Lx7MOq_thSortable", "title": "Lx7MOq_title", "toolbar": "Lx7MOq_toolbar" };
|
|
2147
2152
|
|
|
2148
2153
|
// src/client/RequestLogPage.tsx
|
|
2149
2154
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -2183,7 +2188,7 @@ function sortValue(entry, key) {
|
|
|
2183
2188
|
case "durationMs":
|
|
2184
2189
|
return entry.durationMs ?? -1;
|
|
2185
2190
|
case "status":
|
|
2186
|
-
return entry.ok ? `ok-${entry.status ?? ""}` : `fail-${entry.code ?? ""}`;
|
|
2191
|
+
return entry.running === true ? "running" : entry.ok ? `ok-${entry.status ?? ""}` : `fail-${entry.code ?? ""}`;
|
|
2187
2192
|
}
|
|
2188
2193
|
}
|
|
2189
2194
|
var PAGE_SIZE = 50;
|
|
@@ -2233,8 +2238,10 @@ function groupKey(entry, groupBy) {
|
|
|
2233
2238
|
return entry.session === "" ? "\u2014" : entry.session;
|
|
2234
2239
|
case "model":
|
|
2235
2240
|
return entry.model;
|
|
2241
|
+
// A running row is neither a success nor a failure yet: it groups on
|
|
2242
|
+
// its own instead of inflating either outcome.
|
|
2236
2243
|
case "result":
|
|
2237
|
-
return entry.ok ? "ok" : entry.code ?? "fail";
|
|
2244
|
+
return entry.running === true ? "running" : entry.ok ? "ok" : entry.code ?? "fail";
|
|
2238
2245
|
case "none":
|
|
2239
2246
|
return void 0;
|
|
2240
2247
|
}
|
|
@@ -2276,6 +2283,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2276
2283
|
const title = catalog?.byId[id4]?.title?.trim();
|
|
2277
2284
|
return title === void 0 || title === "" ? { text: id4 === "" ? "\u2014" : id4, titled: false } : { text: title, titled: true };
|
|
2278
2285
|
}, [catalog]);
|
|
2286
|
+
const resultGroupLabel = (group) => group === "ok" ? t("requestLogResultOk") : group === "fail" ? t("requestLogResultFail") : group === "running" ? t("requestLogRunning") : group;
|
|
2279
2287
|
const toggleSort = (key) => {
|
|
2280
2288
|
if (key === sortKey) {
|
|
2281
2289
|
setSortDir(sortDir === "asc" ? "desc" : "asc");
|
|
@@ -2301,6 +2309,16 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2301
2309
|
(0, import_react2.useEffect)(() => {
|
|
2302
2310
|
void load();
|
|
2303
2311
|
}, [load]);
|
|
2312
|
+
const hasRunning = (snapshot?.entries ?? []).some((entry) => entry.running === true);
|
|
2313
|
+
(0, import_react2.useEffect)(() => {
|
|
2314
|
+
if (!hasRunning) return;
|
|
2315
|
+
const timer = setInterval(() => {
|
|
2316
|
+
void load();
|
|
2317
|
+
}, 1e3);
|
|
2318
|
+
return () => {
|
|
2319
|
+
clearInterval(timer);
|
|
2320
|
+
};
|
|
2321
|
+
}, [hasRunning, load]);
|
|
2304
2322
|
const models = (0, import_react2.useMemo)(() => {
|
|
2305
2323
|
const ids = /* @__PURE__ */ new Set();
|
|
2306
2324
|
for (const entry of snapshot?.entries ?? []) ids.add(entry.model);
|
|
@@ -2311,7 +2329,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2311
2329
|
return (snapshot?.entries ?? []).filter((entry) => {
|
|
2312
2330
|
if (model !== "all" && entry.model !== model) return false;
|
|
2313
2331
|
if (result === "ok" && !entry.ok) return false;
|
|
2314
|
-
if (result === "fail" && entry.ok) return false;
|
|
2332
|
+
if (result === "fail" && (entry.ok || entry.running === true)) return false;
|
|
2315
2333
|
if (slowOnly && (entry.durationMs ?? 0) < Math.max(0, slowSeconds) * 1e3) return false;
|
|
2316
2334
|
if (onlySession && sessionId !== void 0 && entry.session !== sessionId) return false;
|
|
2317
2335
|
if (query !== "") {
|
|
@@ -2334,7 +2352,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2334
2352
|
return next;
|
|
2335
2353
|
}, [filtered, sortKey, sortDir]);
|
|
2336
2354
|
const pageEntries = sorted.slice(safePage * PAGE_SIZE, (safePage + 1) * PAGE_SIZE);
|
|
2337
|
-
const failures = filtered.filter((entry) => !entry.ok).length;
|
|
2355
|
+
const failures = filtered.filter((entry) => !entry.ok && entry.running !== true).length;
|
|
2338
2356
|
const meanLatency = average(filtered.flatMap((entry) => entry.durationMs === void 0 ? [] : [entry.durationMs]));
|
|
2339
2357
|
const meanFirstToken = average(filtered.flatMap((entry) => entry.firstTokenMs === void 0 ? [] : [entry.firstTokenMs]));
|
|
2340
2358
|
const meanTps = average(filtered.flatMap((entry) => entry.tps === void 0 ? [] : [entry.tps]));
|
|
@@ -2357,10 +2375,10 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2357
2375
|
if (group !== void 0 && group !== lastGroup) {
|
|
2358
2376
|
lastGroup = group;
|
|
2359
2377
|
out.push(
|
|
2360
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.group, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 11, children: groupBy === "session" ? sessionLabel(group).text : group }) }, `group-${group}-${entry.seq}`)
|
|
2378
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.group, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 11, children: groupBy === "session" ? sessionLabel(group).text : groupBy === "result" ? resultGroupLabel(group) : group }) }, `group-${group}-${entry.seq}`)
|
|
2361
2379
|
);
|
|
2362
2380
|
}
|
|
2363
|
-
const state = !entry.ok ? "rowFail" : entry.tps !== void 0 && entry.tps < 5 ? "rowSlow" : "rowOk";
|
|
2381
|
+
const state = entry.running === true ? "rowRunning" : !entry.ok ? "rowFail" : entry.tps !== void 0 && entry.tps < 5 ? "rowSlow" : "rowOk";
|
|
2364
2382
|
const session = sessionLabel(entry.session);
|
|
2365
2383
|
out.push(
|
|
2366
2384
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
@@ -2398,8 +2416,8 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
|
|
|
2398
2416
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: percent(cacheRate(entry)) }),
|
|
2399
2417
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: rate2(entry.tps) }),
|
|
2400
2418
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: duration(entry.firstTokenMs) }),
|
|
2401
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: duration(entry.durationMs) }),
|
|
2402
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.ok ? entry.status === void 0 ? "\u2713" : String(entry.status) : entry.code ?? "\u2717" })
|
|
2419
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.running === true ? duration(Math.max(0, Date.now() - entry.atMs)) : duration(entry.durationMs) }),
|
|
2420
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.running === true ? `${t("requestLogRunning")} \xB7 ${duration(Math.max(0, Date.now() - entry.atMs))}` : entry.ok ? entry.status === void 0 ? "\u2713" : String(entry.status) : entry.code ?? "\u2717" })
|
|
2403
2421
|
]
|
|
2404
2422
|
},
|
|
2405
2423
|
entry.seq
|
|
@@ -2962,19 +2980,19 @@ function apply(ctx) {
|
|
|
2962
2980
|
scope.effect(() => () => {
|
|
2963
2981
|
controller.dispose();
|
|
2964
2982
|
});
|
|
2983
|
+
const isLogEnabled = () => {
|
|
2984
|
+
if (profile === void 0) return true;
|
|
2985
|
+
const value = profile.getSnapshot().value;
|
|
2986
|
+
if (value === null || typeof value !== "object") return false;
|
|
2987
|
+
return value.requestLogEnabled === true;
|
|
2988
|
+
};
|
|
2965
2989
|
const requestLog = {
|
|
2966
2990
|
readLog: async () => {
|
|
2967
2991
|
const result = await scope.remote.opencodeGoRequestLog.readLog();
|
|
2968
2992
|
if (!result.ok) throw result.error;
|
|
2969
2993
|
return result.value;
|
|
2970
2994
|
},
|
|
2971
|
-
|
|
2972
|
-
// page sits open, and the page says so rather than pretending.
|
|
2973
|
-
enabled: () => {
|
|
2974
|
-
const value = profile?.getSnapshot().value;
|
|
2975
|
-
if (value === null || typeof value !== "object") return false;
|
|
2976
|
-
return value.requestLogEnabled === true;
|
|
2977
|
-
},
|
|
2995
|
+
enabled: isLogEnabled,
|
|
2978
2996
|
// Whether a title click can open its session. The navigation service
|
|
2979
2997
|
// is a separate composition member; without it titles stay text.
|
|
2980
2998
|
canOpenSession: () => sessionOpener.current !== void 0,
|
|
@@ -2995,15 +3013,34 @@ function apply(ctx) {
|
|
|
2995
3013
|
t: (key) => translate(key),
|
|
2996
3014
|
getLocale: () => scope.locale.getLocale().active
|
|
2997
3015
|
};
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3016
|
+
const gateLogSeat = (slot, register) => {
|
|
3017
|
+
scope.slots.inject(slot, () => {
|
|
3018
|
+
let disposeEntry;
|
|
3019
|
+
const sync = () => {
|
|
3020
|
+
if (isLogEnabled()) {
|
|
3021
|
+
disposeEntry ??= register();
|
|
3022
|
+
} else {
|
|
3023
|
+
disposeEntry?.();
|
|
3024
|
+
disposeEntry = void 0;
|
|
3025
|
+
}
|
|
3026
|
+
};
|
|
3027
|
+
const unsubscribe = profile?.subscribe(sync) ?? (() => {
|
|
3028
|
+
});
|
|
3029
|
+
sync();
|
|
3030
|
+
return () => {
|
|
3031
|
+
unsubscribe();
|
|
3032
|
+
disposeEntry?.();
|
|
3033
|
+
disposeEntry = void 0;
|
|
3034
|
+
};
|
|
3035
|
+
});
|
|
3036
|
+
};
|
|
3037
|
+
gateLogSeat("main", () => scope.slots.register({
|
|
3038
|
+
name: "main",
|
|
3039
|
+
key: REQUEST_LOG_PANEL,
|
|
3040
|
+
locale: NS,
|
|
3041
|
+
inject: () => requestLog
|
|
3042
|
+
}, RequestLogPage));
|
|
3043
|
+
gateLogSeat("sidebar.panellist", () => scope.slots.register({
|
|
3007
3044
|
name: "sidebar.panellist",
|
|
3008
3045
|
id: REQUEST_LOG_PANEL,
|
|
3009
3046
|
// After the Host's own panels, which run from 0.
|
|
@@ -3011,7 +3048,7 @@ function apply(ctx) {
|
|
|
3011
3048
|
label: () => translate("requestLogTitle"),
|
|
3012
3049
|
locale: NS
|
|
3013
3050
|
}, RequestLogPanelIcon));
|
|
3014
|
-
|
|
3051
|
+
gateLogSeat("conversation.view", () => scope.slots.register({
|
|
3015
3052
|
name: "conversation.view",
|
|
3016
3053
|
id: "dshopencodego-request-log",
|
|
3017
3054
|
// After the Host's own View (the chat itself), which runs at 0.
|
package/lib/index.js
CHANGED
|
@@ -75876,11 +75876,13 @@ var fields = {
|
|
|
75876
75876
|
maxTokens: Schema.union([Schema.const(null), Schema.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER)])
|
|
75877
75877
|
})])).default({}),
|
|
75878
75878
|
modelProtocols: Schema.dict(Schema.string()).default({}),
|
|
75879
|
-
//
|
|
75880
|
-
// plus a
|
|
75881
|
-
//
|
|
75882
|
-
//
|
|
75883
|
-
|
|
75879
|
+
// Off by default: the log is a troubleshooting tool, and an unasked
|
|
75880
|
+
// sidebar entry plus a conversation tab is the wrong first impression.
|
|
75881
|
+
// On, recording costs one call and one boolean per request plus a bounded
|
|
75882
|
+
// ring, and buys a per-request trail whenever something looks wrong. Off
|
|
75883
|
+
// returns the request path to exactly as without the log, because the
|
|
75884
|
+
// recorder is never constructed then.
|
|
75885
|
+
requestLogEnabled: Schema.boolean().default(false),
|
|
75884
75886
|
requestLogDebug: Schema.boolean().default(false),
|
|
75885
75887
|
requestLogMaxEntries: Schema.number().step(1).min(MIN_REQUEST_LOG_MAX_ENTRIES).max(MAX_REQUEST_LOG_MAX_ENTRIES).default(DEFAULT_REQUEST_LOG_MAX_ENTRIES),
|
|
75886
75888
|
pinnedAddresses: Schema.dict(Schema.string()).default({}),
|
|
@@ -77066,6 +77068,7 @@ var OpencodeGoAdapter = class extends LlmAdapter {
|
|
|
77066
77068
|
} finally {
|
|
77067
77069
|
if (!exhausted) {
|
|
77068
77070
|
timedOut = timeoutOf(watchdog.signal, "LLM_STREAM_IDLE_TIMEOUT") !== void 0;
|
|
77071
|
+
span?.finish(false, "ABORTED");
|
|
77069
77072
|
consumer.abort("opencode-go stream consumer stopped");
|
|
77070
77073
|
try {
|
|
77071
77074
|
void Promise.resolve(iterator.return(void 0)).catch(() => {
|
|
@@ -77319,6 +77322,9 @@ function parseEntry(value) {
|
|
|
77319
77322
|
...row["decodeMs"] === void 0 ? {} : { decodeMs: count("decodeMs", row) },
|
|
77320
77323
|
...row["streamMs"] === void 0 ? {} : { streamMs: count("streamMs", row) },
|
|
77321
77324
|
...row["tps"] === void 0 ? {} : { tps: rate("tps", row) },
|
|
77325
|
+
// Running rows cross the wire too: a viewer opened mid-request must
|
|
77326
|
+
// see them. Older snapshots predate the flag and read as finished.
|
|
77327
|
+
...row["running"] === true ? { running: true } : {},
|
|
77322
77328
|
ok: row["ok"] === true,
|
|
77323
77329
|
...typeof row["code"] === "string" ? { code: row["code"] } : {},
|
|
77324
77330
|
...typeof row["poolKey"] === "string" ? { poolKey: row["poolKey"] } : {},
|
|
@@ -77895,52 +77901,77 @@ var RequestLog = class {
|
|
|
77895
77901
|
* Begin one request's span, or nothing when the log is disabled.
|
|
77896
77902
|
*
|
|
77897
77903
|
* This is the single gate on the request path: a disabled log costs one
|
|
77898
|
-
* method call and one boolean read, and constructs nothing.
|
|
77904
|
+
* method call and one boolean read, and constructs nothing. When enabled,
|
|
77905
|
+
* the running row is appended before the span is returned, so the viewer
|
|
77906
|
+
* sees the request while it runs; `request` and `status` mutate that same
|
|
77907
|
+
* visible row as the wire facts arrive.
|
|
77899
77908
|
*/
|
|
77900
77909
|
start(detail) {
|
|
77901
77910
|
if (!this.gates.enabled()) return void 0;
|
|
77902
77911
|
const debug = this.gates.debug();
|
|
77903
77912
|
const startedAt = Date.now();
|
|
77904
77913
|
const session = detail.sessionId ?? "";
|
|
77914
|
+
const record2 = {
|
|
77915
|
+
seq: ++this.seq,
|
|
77916
|
+
atMs: startedAt,
|
|
77917
|
+
model: detail.model,
|
|
77918
|
+
...detail.protocol === void 0 ? {} : { protocol: detail.protocol },
|
|
77919
|
+
session,
|
|
77920
|
+
method: detail.method ?? "POST",
|
|
77921
|
+
url: detail.url ?? "",
|
|
77922
|
+
running: true,
|
|
77923
|
+
ok: false,
|
|
77924
|
+
shape: { messages: detail.messageCount, hasImage: detail.hasImage, tools: detail.toolCount },
|
|
77925
|
+
...detail.imageBytes === void 0 ? {} : { imageBytes: detail.imageBytes },
|
|
77926
|
+
...!debug ? {} : {
|
|
77927
|
+
headers: {
|
|
77928
|
+
...detail.headers.session === void 0 ? {} : { session: detail.headers.session },
|
|
77929
|
+
...detail.headers.userAgent === void 0 ? {} : { userAgent: detail.headers.userAgent }
|
|
77930
|
+
}
|
|
77931
|
+
}
|
|
77932
|
+
};
|
|
77933
|
+
this.insert(record2, true);
|
|
77905
77934
|
let firstTokenAt;
|
|
77906
77935
|
let lastTokenAt;
|
|
77907
|
-
let
|
|
77908
|
-
let url;
|
|
77909
|
-
let requestBytes;
|
|
77910
|
-
let status;
|
|
77936
|
+
let reported = false;
|
|
77911
77937
|
let finished = false;
|
|
77912
77938
|
return {
|
|
77913
77939
|
token: () => {
|
|
77914
77940
|
const now = Date.now();
|
|
77915
|
-
if (firstTokenAt === void 0)
|
|
77941
|
+
if (firstTokenAt === void 0) {
|
|
77942
|
+
firstTokenAt = now;
|
|
77943
|
+
record2.firstTokenMs = Math.max(0, now - startedAt);
|
|
77944
|
+
}
|
|
77916
77945
|
lastTokenAt = now;
|
|
77917
77946
|
},
|
|
77918
77947
|
request: (nextMethod, nextUrl, nextRequestBytes) => {
|
|
77919
|
-
if (
|
|
77920
|
-
|
|
77921
|
-
|
|
77922
|
-
|
|
77948
|
+
if (!reported) {
|
|
77949
|
+
reported = true;
|
|
77950
|
+
record2.method = nextMethod;
|
|
77951
|
+
record2.url = nextUrl;
|
|
77952
|
+
if (nextRequestBytes !== void 0) record2.requestBytes = nextRequestBytes;
|
|
77923
77953
|
}
|
|
77924
77954
|
},
|
|
77925
77955
|
status: (value) => {
|
|
77926
|
-
status = value;
|
|
77956
|
+
record2.status = value;
|
|
77927
77957
|
},
|
|
77928
77958
|
finish: (ok, code, usage, costUsd) => {
|
|
77929
77959
|
if (finished) return;
|
|
77930
77960
|
finished = true;
|
|
77961
|
+
const seq = record2.seq;
|
|
77931
77962
|
const endedAt = Date.now();
|
|
77932
77963
|
const decodeMs = firstTokenAt === void 0 ? void 0 : Math.max(0, endedAt - firstTokenAt);
|
|
77933
77964
|
const streamMs = firstTokenAt === void 0 || lastTokenAt === void 0 ? void 0 : Math.max(0, lastTokenAt - firstTokenAt);
|
|
77934
77965
|
const tps = decodeMs === void 0 || decodeMs <= 0 || usage === void 0 || streamMs === void 0 || streamMs < MIN_STREAM_MS_FOR_RATE ? void 0 : usage.outputTokens / (decodeMs / 1e3);
|
|
77935
77966
|
const entry = {
|
|
77936
|
-
seq
|
|
77967
|
+
seq,
|
|
77937
77968
|
atMs: startedAt,
|
|
77938
77969
|
model: detail.model,
|
|
77939
77970
|
...detail.protocol === void 0 ? {} : { protocol: detail.protocol },
|
|
77940
77971
|
session,
|
|
77941
|
-
method:
|
|
77942
|
-
url:
|
|
77943
|
-
...status === void 0 ? {} : { status },
|
|
77972
|
+
method: record2.method,
|
|
77973
|
+
url: record2.url,
|
|
77974
|
+
...record2.status === void 0 ? {} : { status: record2.status },
|
|
77944
77975
|
...usage === void 0 ? {} : {
|
|
77945
77976
|
inputTokens: usage.inputTokens,
|
|
77946
77977
|
outputTokens: usage.outputTokens,
|
|
@@ -77949,7 +77980,7 @@ var RequestLog = class {
|
|
|
77949
77980
|
...usage.reasoningTokens === void 0 || usage.reasoningTokens <= 0 ? {} : { reasoningTokens: usage.reasoningTokens }
|
|
77950
77981
|
},
|
|
77951
77982
|
...costUsd === void 0 ? {} : { costUsd },
|
|
77952
|
-
...requestBytes === void 0 ? {} : { requestBytes },
|
|
77983
|
+
...record2.requestBytes === void 0 ? {} : { requestBytes: record2.requestBytes },
|
|
77953
77984
|
...detail.imageBytes === void 0 ? {} : { imageBytes: detail.imageBytes },
|
|
77954
77985
|
durationMs: Math.max(0, endedAt - startedAt),
|
|
77955
77986
|
...firstTokenAt === void 0 ? {} : { firstTokenMs: Math.max(0, firstTokenAt - startedAt) },
|
|
@@ -77970,12 +78001,17 @@ var RequestLog = class {
|
|
|
77970
78001
|
}
|
|
77971
78002
|
}
|
|
77972
78003
|
};
|
|
77973
|
-
this.
|
|
78004
|
+
const at = this.entries.findIndex((candidate) => candidate.seq === seq);
|
|
78005
|
+
if (at === -1) {
|
|
78006
|
+
this.insert(entry, false);
|
|
78007
|
+
} else {
|
|
78008
|
+
this.entries[at] = entry;
|
|
78009
|
+
}
|
|
77974
78010
|
}
|
|
77975
78011
|
};
|
|
77976
78012
|
}
|
|
77977
|
-
|
|
77978
|
-
this.totalRecords += 1;
|
|
78013
|
+
insert(entry, count2) {
|
|
78014
|
+
if (count2) this.totalRecords += 1;
|
|
77979
78015
|
const bound = Math.max(1, Math.floor(this.gates.maxEntries()));
|
|
77980
78016
|
const next = [entry, ...this.entries].slice(0, bound);
|
|
77981
78017
|
this.droppedRecords += this.entries.length + 1 - next.length;
|
|
@@ -78095,7 +78131,7 @@ function createUpdateChecker(deps) {
|
|
|
78095
78131
|
}
|
|
78096
78132
|
|
|
78097
78133
|
// src/update/version.ts
|
|
78098
|
-
var PLUGIN_VERSION = true ? "0.1.
|
|
78134
|
+
var PLUGIN_VERSION = true ? "0.1.78" : "0.0.0-dev";
|
|
78099
78135
|
|
|
78100
78136
|
// src/update/service.ts
|
|
78101
78137
|
import { TypertRemoteService as TypertRemoteService4 } from "@deepseek-ai/dsh-typert-protocol";
|
package/lib/types/config.d.ts
CHANGED
|
@@ -77,10 +77,10 @@ export interface OpencodeGoConfig {
|
|
|
77
77
|
*/
|
|
78
78
|
modelProtocols: Record<string, string>;
|
|
79
79
|
/**
|
|
80
|
-
* Whether the request log records anything at all.
|
|
81
|
-
* call and one boolean per request, plus a bounded ring
|
|
82
|
-
* request path to exactly as without the log — the
|
|
83
|
-
* constructed.
|
|
80
|
+
* Whether the request log records anything at all. Off by default: one
|
|
81
|
+
* call and one boolean per request, plus a bounded ring, only when on.
|
|
82
|
+
* Off returns the request path to exactly as without the log — the
|
|
83
|
+
* recorder is never constructed, and the log's pages stay unregistered.
|
|
84
84
|
*/
|
|
85
85
|
requestLogEnabled: boolean;
|
|
86
86
|
/**
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Everything here is inert unless the profile enables it. The recorder is
|
|
5
5
|
* constructed only when `requestLogEnabled` reads true, so a disabled log
|
|
6
6
|
* changes nothing on the request path — not one extra allocation. When
|
|
7
|
-
* enabled,
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* enabled, `start` appends a running placeholder immediately — so a request
|
|
8
|
+
* is visible from the moment it begins, not only after it ends — and
|
|
9
|
+
* `finish` completes that same row in place. The oldest entries are dropped
|
|
10
|
+
* past the configured bound, so the log can never grow with the session.
|
|
10
11
|
*
|
|
11
12
|
* Two facts are structural rather than conventional:
|
|
12
13
|
*
|
|
@@ -101,7 +102,16 @@ export interface RequestLogEntry {
|
|
|
101
102
|
* misreport a buffered reply as infinitely quick.
|
|
102
103
|
*/
|
|
103
104
|
readonly tps?: number;
|
|
104
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Whether the request is still in flight. Present only while it runs:
|
|
107
|
+
* `finish` removes it. A running row always reads `ok: false` with no
|
|
108
|
+
* `code`, so readers must check this flag before counting a failure.
|
|
109
|
+
*/
|
|
110
|
+
readonly running?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Whether the request completed successfully. Always false while
|
|
113
|
+
* `running` is true.
|
|
114
|
+
*/
|
|
105
115
|
readonly ok: boolean;
|
|
106
116
|
/** Failure code (`TIMEOUT`, `TRANSPORT`, …) for a request that did not complete. */
|
|
107
117
|
readonly code?: string;
|
|
@@ -136,7 +146,7 @@ export interface PoolLogEvent {
|
|
|
136
146
|
}
|
|
137
147
|
/** What the viewer reads: the entries plus the counters a rate needs. */
|
|
138
148
|
export interface RequestLogSnapshot {
|
|
139
|
-
/** Newest first, at most the configured bound. */
|
|
149
|
+
/** Newest first, at most the configured bound — finished rows plus running ones. */
|
|
140
150
|
readonly entries: readonly RequestLogEntry[];
|
|
141
151
|
/** Recent pool lifecycle, newest first, capped. */
|
|
142
152
|
readonly poolEvents: readonly PoolLogEvent[];
|
|
@@ -161,11 +171,12 @@ export interface RequestLogGates {
|
|
|
161
171
|
/**
|
|
162
172
|
* An in-flight request, handed back only when the log is enabled.
|
|
163
173
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* already
|
|
174
|
+
* `start` already appended its running row, so every method mutates the
|
|
175
|
+
* visible entry in place. Every method stays cheap by construction: one
|
|
176
|
+
* `Date.now` read per content delta, field writes for the wire line, and
|
|
177
|
+
* small integer arithmetic at close. No serialization, no hashing, no body
|
|
178
|
+
* handling — the counts ride the usage chunk the adapter already holds,
|
|
179
|
+
* and the timings bracket work that already happened.
|
|
169
180
|
*/
|
|
170
181
|
export interface RequestSpan {
|
|
171
182
|
/**
|
|
@@ -188,7 +199,7 @@ export interface RequestSpan {
|
|
|
188
199
|
/** Mark the response status line's arrival. */
|
|
189
200
|
status(status: number): void;
|
|
190
201
|
/**
|
|
191
|
-
* Close the span and
|
|
202
|
+
* Close the span and complete its running row in place.
|
|
192
203
|
* @param ok - whether the request completed.
|
|
193
204
|
* @param code - failure code for a request that did not complete.
|
|
194
205
|
* @param usage - provider-reported counts, when the terminal usage arrived.
|
|
@@ -254,10 +265,13 @@ export declare class RequestLog {
|
|
|
254
265
|
* Begin one request's span, or nothing when the log is disabled.
|
|
255
266
|
*
|
|
256
267
|
* This is the single gate on the request path: a disabled log costs one
|
|
257
|
-
* method call and one boolean read, and constructs nothing.
|
|
268
|
+
* method call and one boolean read, and constructs nothing. When enabled,
|
|
269
|
+
* the running row is appended before the span is returned, so the viewer
|
|
270
|
+
* sees the request while it runs; `request` and `status` mutate that same
|
|
271
|
+
* visible row as the wire facts arrive.
|
|
258
272
|
*/
|
|
259
273
|
start(detail: RequestStart): RequestSpan | undefined;
|
|
260
|
-
private
|
|
274
|
+
private insert;
|
|
261
275
|
/** Shrink the ring to a new bound immediately; growing backfills nothing. */
|
|
262
276
|
setMaxEntries(bound: number): void;
|
|
263
277
|
/** The current reading, newest first. Computed lazily: nothing on the hot path. */
|
package/package.json
CHANGED