opencode-subagent-magazine 1.6.0 → 1.7.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 +12 -1
- package/dist/_version.d.ts +1 -1
- package/dist/_version.js +1 -1
- package/dist/core/format.d.ts +7 -0
- package/dist/core/format.js +45 -0
- package/dist/core/kv.d.ts +21 -1
- package/dist/core/kv.js +33 -1
- package/dist/core/types.d.ts +12 -0
- package/dist/i18n.d.ts +19 -0
- package/dist/i18n.js +76 -0
- package/dist/index.js +73 -2
- package/dist/panel/SubAgentPanel.d.ts +5 -1
- package/dist/panel/SubAgentPanel.js +568 -261
- package/dist/panel/entry-map.d.ts +16 -0
- package/dist/panel/entry-map.js +74 -0
- package/dist/panel/panel-api.d.ts +8 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +8 -0
- package/dist/tui.js +927 -431
- package/dist/v2/commands.js +188 -44
- package/dist/v2/db.d.ts +47 -0
- package/dist/v2/db.js +267 -0
- package/dist/v2/index.js +26 -6
- package/dist/v2/settings-menu.d.ts +9 -0
- package/dist/v2/settings-menu.js +225 -0
- package/dist/v2/v2-panel-api.js +304 -44
- package/dist/v2.js +1741 -560
- package/package.json +1 -1
- package/src/_version.ts +1 -1
- package/src/core/format.ts +44 -0
- package/src/core/kv.ts +42 -2
- package/src/core/types.ts +12 -0
- package/src/i18n.ts +76 -0
- package/src/index.tsx +93 -2
- package/src/panel/SubAgentPanel.tsx +541 -233
- package/src/panel/entry-map.ts +77 -1
- package/src/panel/panel-api.ts +8 -0
- package/src/server.ts +10 -1
- package/src/v2/commands.ts +187 -43
- package/src/v2/db.ts +274 -0
- package/src/v2/index.tsx +49 -7
- package/src/v2/settings-menu.ts +241 -0
- package/src/v2/types.ts +3 -1
- package/src/v2/v2-panel-api.ts +241 -33
package/README.md
CHANGED
|
@@ -57,7 +57,12 @@
|
|
|
57
57
|
- **手动清除条目**:`/subagent-clear-entries` 清除当前会话所有记录,防止历史条目扫描重建
|
|
58
58
|
- **语言适配**:支持 `/subagent-lang` 运行时切换中/英文,偏好持久化
|
|
59
59
|
- **可切换边框**:`/subagent-border` 控制面板外边框显隐(默认隐藏),开启后与缓存统计面板风格统一
|
|
60
|
-
-
|
|
60
|
+
- **多实例安全**:多开 TUI 时条目不再互相覆盖(原子合并写入)
|
|
61
|
+
- **漏事件兜底**:周期性校正 + 历史扫描 + 发现轮询,掉事件的子代理也能恢复显示
|
|
62
|
+
- **条目显示开关**:`/subagent-cost` 控制列表内条目费用(4 位小数,默认关;展开区始终显示);`/subagent-time`、`/subagent-tokens` 控制耗时与 Token(默认开,保持原有显示)
|
|
63
|
+
- **耗时格式**:`/subagent-time-format` 可选 short(默认)/ decimal / clock / compact / seconds
|
|
64
|
+
- **设置菜单**:`/subagent-sections` 打开原生循环设置菜单(V2);`/subagent-config` 查看当前设置摘要
|
|
65
|
+
- **斜杠命令**:`/subagent-lang` `/subagent-max` `/subagent-order` `/subagent-scroll` `/subagent-ttl` `/subagent-clear-entries` `/subagent-session` `/subagent-version` `/subagent-clear-running` `/subagent-border` `/subagent-config` `/subagent-sections` `/subagent-cost` `/subagent-time` `/subagent-tokens` `/subagent-time-format` 动态配置
|
|
61
66
|
|
|
62
67
|
---
|
|
63
68
|
|
|
@@ -152,6 +157,12 @@ npm install -g opencode-subagent-magazine@latest
|
|
|
152
157
|
| `/subagent-version` | 查看插件版本 | 弹出当前插件版本号 |
|
|
153
158
|
| `/subagent-clear-running` | 批量清理僵尸条目 | 一键将所有运行中的条目标记为完成,清理卡住的旧数据 |
|
|
154
159
|
| `/subagent-border` | 开关面板边框 | 切换面板外边框显隐(默认隐藏),即时生效并持久化 |
|
|
160
|
+
| `/subagent-config` | 查看设置摘要 | 弹窗列出语言、条目数、排序、翻页、保留期、边框与各显示开关的当前值 |
|
|
161
|
+
| `/subagent-sections` | 打开设置菜单 | 原生选择列表逐项修改,修改后自动回到菜单(Esc 关闭) |
|
|
162
|
+
| `/subagent-cost` | 开关条目费用 | 列表内条目费用显示(4 位小数,默认关;展开区始终显示) |
|
|
163
|
+
| `/subagent-time` | 开关条目耗时 | 列表内耗时显示(默认开) |
|
|
164
|
+
| `/subagent-tokens` | 开关条目 Token | 列表内 Token 显示(默认开) |
|
|
165
|
+
| `/subagent-time-format` | 切换耗时格式 | short(默认 45.32s / 112m35s)/ decimal / clock / compact / seconds |
|
|
155
166
|
|
|
156
167
|
<div align="center">
|
|
157
168
|
<img src="https://raw.githubusercontent.com/Hotakus/opencode-subagent-magazine/master/assets/slash_cmds.png" alt="斜杠命令" width="49%"></img>
|
package/dist/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PLUGIN_VERSION = "1.
|
|
1
|
+
export declare const PLUGIN_VERSION = "1.7.0";
|
package/dist/_version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// auto-generated
|
|
2
|
-
export const PLUGIN_VERSION = "1.
|
|
2
|
+
export const PLUGIN_VERSION = "1.7.0";
|
package/dist/core/format.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import type { TimeFormat } from "./types";
|
|
1
2
|
export declare function visualWidth(s: string): number;
|
|
2
3
|
export declare function truncate(text: string, maxCols: number): string;
|
|
3
4
|
export declare function fmtDurationShort(ms: number, running: boolean): string;
|
|
5
|
+
/** All supported elapsed-time display modes, in picker order. */
|
|
6
|
+
export declare const TIME_FORMATS: readonly TimeFormat[];
|
|
7
|
+
/** Neutral samples shown in the time-format picker (language independent). */
|
|
8
|
+
export declare const TIME_FORMAT_SAMPLES: Record<TimeFormat, string>;
|
|
9
|
+
/** Formats elapsed milliseconds using the configured display mode. */
|
|
10
|
+
export declare function fmtDuration(ms: number, running: boolean, mode?: TimeFormat): string;
|
|
4
11
|
export declare function fmtTokens(n: number): string;
|
|
5
12
|
export declare function safeErrorMsg(err: unknown): string;
|
package/dist/core/format.js
CHANGED
|
@@ -50,6 +50,51 @@ export function fmtDurationShort(ms, running) {
|
|
|
50
50
|
const s = Math.round((ms % 60000) / 1000);
|
|
51
51
|
return `${m}m${s}s`;
|
|
52
52
|
}
|
|
53
|
+
/** All supported elapsed-time display modes, in picker order. */
|
|
54
|
+
export const TIME_FORMATS = ["short", "decimal", "clock", "compact", "seconds"];
|
|
55
|
+
/** Neutral samples shown in the time-format picker (language independent). */
|
|
56
|
+
export const TIME_FORMAT_SAMPLES = {
|
|
57
|
+
decimal: "45.3s · 9.5m · 1.9h",
|
|
58
|
+
short: "45.32s · 112m35s",
|
|
59
|
+
clock: "0:45 · 1:52:35",
|
|
60
|
+
compact: "45s · 1h52m",
|
|
61
|
+
seconds: "45s · 6755s",
|
|
62
|
+
};
|
|
63
|
+
/** Formats elapsed milliseconds using the configured display mode. */
|
|
64
|
+
export function fmtDuration(ms, running, mode = "short") {
|
|
65
|
+
if (running && ms < 2000)
|
|
66
|
+
return "";
|
|
67
|
+
const t = Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
68
|
+
switch (mode) {
|
|
69
|
+
case "short":
|
|
70
|
+
return fmtDurationShort(t, false);
|
|
71
|
+
case "compact": {
|
|
72
|
+
if (t < 60000)
|
|
73
|
+
return `${Math.floor(t / 1000)}s`;
|
|
74
|
+
if (t < 3600000)
|
|
75
|
+
return `${Math.floor(t / 60000)}m${Math.floor((t % 60000) / 1000)}s`;
|
|
76
|
+
return `${Math.floor(t / 3600000)}h${Math.floor((t % 3600000) / 60000)}m`;
|
|
77
|
+
}
|
|
78
|
+
case "clock": {
|
|
79
|
+
const s = Math.floor((t % 60000) / 1000);
|
|
80
|
+
if (t < 3600000)
|
|
81
|
+
return `${Math.floor(t / 60000)}:${String(s).padStart(2, "0")}`;
|
|
82
|
+
const m = Math.floor((t % 3600000) / 60000);
|
|
83
|
+
return `${Math.floor(t / 3600000)}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
84
|
+
}
|
|
85
|
+
case "seconds":
|
|
86
|
+
return `${Math.floor(t / 1000)}s`;
|
|
87
|
+
case "decimal":
|
|
88
|
+
default: {
|
|
89
|
+
// Promote units when rounding would show "60.0s" / "60.0m".
|
|
90
|
+
if (t < 59950)
|
|
91
|
+
return `${(t / 1000).toFixed(1)}s`;
|
|
92
|
+
if (t < 3597000)
|
|
93
|
+
return `${(t / 60000).toFixed(1)}m`;
|
|
94
|
+
return `${(t / 3600000).toFixed(1)}h`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
53
98
|
export function fmtTokens(n) {
|
|
54
99
|
if (n < 1000)
|
|
55
100
|
return `${n}`;
|
package/dist/core/kv.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ export declare const SESSION_DATA_KEY = "subagent_magazine.session_data";
|
|
|
5
5
|
export interface KVApi {
|
|
6
6
|
get(key: string, fallback?: unknown): unknown;
|
|
7
7
|
set(key: string, value: unknown): void;
|
|
8
|
+
/**
|
|
9
|
+
* Optional atomic read-modify-write. When the host exposes it (V2), the
|
|
10
|
+
* updater runs under the host storage lock against the latest on-disk value,
|
|
11
|
+
* which is what makes concurrent TUI instances safe: no instance can
|
|
12
|
+
* overwrite another one's fresh writes with a stale in-memory snapshot.
|
|
13
|
+
*/
|
|
14
|
+
update?(key: string, updater: (current: unknown) => unknown): void | Promise<void>;
|
|
8
15
|
}
|
|
9
16
|
/** Setting keys shared by V1/V2 (values are raw strings in KV). */
|
|
10
17
|
export declare const SETTING_KEYS: {
|
|
@@ -15,7 +22,20 @@ export declare const SETTING_KEYS: {
|
|
|
15
22
|
readonly open: "subagent_magazine.open";
|
|
16
23
|
readonly ttlDays: "subagent_magazine.ttl_days";
|
|
17
24
|
readonly border: "subagent_magazine.border";
|
|
25
|
+
readonly showEntryCost: "subagent_magazine.show_entry_cost";
|
|
26
|
+
readonly showEntryTime: "subagent_magazine.show_entry_time";
|
|
27
|
+
readonly showEntryTokens: "subagent_magazine.show_entry_tokens";
|
|
28
|
+
readonly timeFormat: "subagent_magazine.time_format";
|
|
29
|
+
readonly dbSync: "subagent_magazine.db_sync";
|
|
18
30
|
};
|
|
19
31
|
export declare function loadSessionData(kv: KVApi): Record<string, SessionRecord>;
|
|
20
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Atomic read-merge-write of the session store.
|
|
34
|
+
*
|
|
35
|
+
* `mutator` receives the latest persisted map (read under the host lock when
|
|
36
|
+
* `kv.update` is available) and may mutate it freely; the result is written
|
|
37
|
+
* back. Every writer must go through here: writing a whole in-memory snapshot
|
|
38
|
+
* directly would silently drop entries created by the other TUIs.
|
|
39
|
+
*/
|
|
40
|
+
export declare function updateSessionData(kv: KVApi, mutator: (data: Record<string, SessionRecord>) => void): void | Promise<void>;
|
|
21
41
|
export declare function readTTLDays(kv: KVApi): number;
|
package/dist/core/kv.js
CHANGED
|
@@ -9,6 +9,11 @@ export const SETTING_KEYS = {
|
|
|
9
9
|
open: `${KV_PREFIX}.open`,
|
|
10
10
|
ttlDays: `${KV_PREFIX}.ttl_days`,
|
|
11
11
|
border: `${KV_PREFIX}.border`,
|
|
12
|
+
showEntryCost: `${KV_PREFIX}.show_entry_cost`,
|
|
13
|
+
showEntryTime: `${KV_PREFIX}.show_entry_time`,
|
|
14
|
+
showEntryTokens: `${KV_PREFIX}.show_entry_tokens`,
|
|
15
|
+
timeFormat: `${KV_PREFIX}.time_format`,
|
|
16
|
+
dbSync: `${KV_PREFIX}.db_sync`,
|
|
12
17
|
};
|
|
13
18
|
export function loadSessionData(kv) {
|
|
14
19
|
try {
|
|
@@ -19,12 +24,39 @@ export function loadSessionData(kv) {
|
|
|
19
24
|
return {};
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Atomic read-merge-write of the session store.
|
|
29
|
+
*
|
|
30
|
+
* `mutator` receives the latest persisted map (read under the host lock when
|
|
31
|
+
* `kv.update` is available) and may mutate it freely; the result is written
|
|
32
|
+
* back. Every writer must go through here: writing a whole in-memory snapshot
|
|
33
|
+
* directly would silently drop entries created by the other TUIs.
|
|
34
|
+
*/
|
|
35
|
+
export function updateSessionData(kv, mutator) {
|
|
23
36
|
try {
|
|
37
|
+
if (typeof kv.update === "function") {
|
|
38
|
+
return kv.update(SESSION_DATA_KEY, (raw) => {
|
|
39
|
+
const data = parseSessionData(raw);
|
|
40
|
+
mutator(data);
|
|
41
|
+
return JSON.stringify(data);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// Fallback (V1 hosts without atomic update): read-modify-write. The window
|
|
45
|
+
// is small and the mutation is merged against the value we just read.
|
|
46
|
+
const data = loadSessionData(kv);
|
|
47
|
+
mutator(data);
|
|
24
48
|
kv.set(SESSION_DATA_KEY, JSON.stringify(data));
|
|
25
49
|
}
|
|
26
50
|
catch { }
|
|
27
51
|
}
|
|
52
|
+
function parseSessionData(raw) {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(String(raw ?? "{}"));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
28
60
|
export function readTTLDays(kv) {
|
|
29
61
|
const ttlDaysRaw = parseInt(String(kv.get(SETTING_KEYS.ttlDays, "3")), 10);
|
|
30
62
|
return Number.isNaN(ttlDaysRaw) ? 3 : ttlDaysRaw;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface SubEntry {
|
|
|
22
22
|
export type Lang = LangCode;
|
|
23
23
|
export type SortOrder = "desc" | "asc";
|
|
24
24
|
export type ScrollMode = "wheel" | "click";
|
|
25
|
+
/** How elapsed time is rendered in the sidebar (see TIME_FORMAT_SAMPLES). */
|
|
26
|
+
export type TimeFormat = "decimal" | "short" | "clock" | "compact" | "seconds";
|
|
25
27
|
/** OpenCode built-in tool names that spawn sub-agents or delegate tasks. */
|
|
26
28
|
export declare const SUBAGENT_TOOLS: Set<string>;
|
|
27
29
|
export interface ChildRecord {
|
|
@@ -49,5 +51,15 @@ export interface SharedSignals {
|
|
|
49
51
|
setScrollMode: (m: ScrollMode) => void;
|
|
50
52
|
borderVisible: () => boolean;
|
|
51
53
|
setBorderVisible: (v: boolean) => void;
|
|
54
|
+
showEntryCost: () => boolean;
|
|
55
|
+
setShowEntryCost: (v: boolean) => void;
|
|
56
|
+
showEntryTime: () => boolean;
|
|
57
|
+
setShowEntryTime: (v: boolean) => void;
|
|
58
|
+
showEntryTokens: () => boolean;
|
|
59
|
+
setShowEntryTokens: (v: boolean) => void;
|
|
60
|
+
timeFormat: () => TimeFormat;
|
|
61
|
+
setTimeFormat: (f: TimeFormat) => void;
|
|
62
|
+
dbSync: () => boolean;
|
|
63
|
+
setDbSync: (v: boolean) => void;
|
|
52
64
|
sessionId: string;
|
|
53
65
|
}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ declare const ZH_T: {
|
|
|
40
40
|
readonly "clear.prompt": "确定清除当前会话所有子代理记录?此操作不可撤销。";
|
|
41
41
|
readonly "clear.prompt_running": "当前有 {n} 个运行中的子代理,清除后将不可恢复。确定继续?";
|
|
42
42
|
readonly "clear.done": "已清除 {n} 条子代理记录";
|
|
43
|
+
readonly "clear.failed": "清除失败";
|
|
43
44
|
readonly "clear.empty": "当前会话无子代理记录";
|
|
44
45
|
readonly "cancel.no_session": "子会话 ID 不可用";
|
|
45
46
|
readonly "cancel.not_child": "目标不是子会话";
|
|
@@ -51,6 +52,24 @@ declare const ZH_T: {
|
|
|
51
52
|
readonly "cancel.failed": "取消失败";
|
|
52
53
|
readonly borderShown: "面板边框 已显示";
|
|
53
54
|
readonly borderHidden: "面板边框 已隐藏";
|
|
55
|
+
readonly "settings.title": "SubAgent Magazine — 设置";
|
|
56
|
+
readonly "settings.section.general": "通用";
|
|
57
|
+
readonly "settings.lang": "语言";
|
|
58
|
+
readonly "settings.max": "最大条目数";
|
|
59
|
+
readonly "settings.order": "排序方式";
|
|
60
|
+
readonly "settings.scroll": "滚动模式";
|
|
61
|
+
readonly "settings.ttl": "保留期限";
|
|
62
|
+
readonly "settings.border": "面板边框";
|
|
63
|
+
readonly "settings.showEntryCost": "每条子代理显示费用";
|
|
64
|
+
readonly "settings.showEntryTime": "列表中显示耗时";
|
|
65
|
+
readonly "settings.showEntryTokens": "列表中显示 Token 数";
|
|
66
|
+
readonly "settings.timeFormat": "时间格式";
|
|
67
|
+
readonly "settings.dbSync": "本地数据库补全";
|
|
68
|
+
readonly "settings.on": "开";
|
|
69
|
+
readonly "settings.off": "关";
|
|
70
|
+
readonly "settings.saved": "已保存 {item}:{value}";
|
|
71
|
+
readonly "settings.invalid": "无效的数值";
|
|
72
|
+
readonly "settings.close": "关闭";
|
|
54
73
|
};
|
|
55
74
|
/** 结构约束:值放宽为 string,键集合来自中文表(新增语言缺 key 会编译报错)。 */
|
|
56
75
|
export type Translation = {
|
package/dist/i18n.js
CHANGED
|
@@ -44,6 +44,7 @@ const ZH_T = {
|
|
|
44
44
|
"clear.prompt": "确定清除当前会话所有子代理记录?此操作不可撤销。",
|
|
45
45
|
"clear.prompt_running": "当前有 {n} 个运行中的子代理,清除后将不可恢复。确定继续?",
|
|
46
46
|
"clear.done": "已清除 {n} 条子代理记录",
|
|
47
|
+
"clear.failed": "清除失败",
|
|
47
48
|
"clear.empty": "当前会话无子代理记录",
|
|
48
49
|
"cancel.no_session": "子会话 ID 不可用",
|
|
49
50
|
"cancel.not_child": "目标不是子会话",
|
|
@@ -55,6 +56,24 @@ const ZH_T = {
|
|
|
55
56
|
"cancel.failed": "取消失败",
|
|
56
57
|
"borderShown": "面板边框 已显示",
|
|
57
58
|
"borderHidden": "面板边框 已隐藏",
|
|
59
|
+
"settings.title": "SubAgent Magazine — 设置",
|
|
60
|
+
"settings.section.general": "通用",
|
|
61
|
+
"settings.lang": "语言",
|
|
62
|
+
"settings.max": "最大条目数",
|
|
63
|
+
"settings.order": "排序方式",
|
|
64
|
+
"settings.scroll": "滚动模式",
|
|
65
|
+
"settings.ttl": "保留期限",
|
|
66
|
+
"settings.border": "面板边框",
|
|
67
|
+
"settings.showEntryCost": "每条子代理显示费用",
|
|
68
|
+
"settings.showEntryTime": "列表中显示耗时",
|
|
69
|
+
"settings.showEntryTokens": "列表中显示 Token 数",
|
|
70
|
+
"settings.timeFormat": "时间格式",
|
|
71
|
+
"settings.dbSync": "本地数据库补全",
|
|
72
|
+
"settings.on": "开",
|
|
73
|
+
"settings.off": "关",
|
|
74
|
+
"settings.saved": "已保存 {item}:{value}",
|
|
75
|
+
"settings.invalid": "无效的数值",
|
|
76
|
+
"settings.close": "关闭",
|
|
58
77
|
};
|
|
59
78
|
const EN_T = {
|
|
60
79
|
"panel.title": "SubAgent",
|
|
@@ -97,6 +116,7 @@ const EN_T = {
|
|
|
97
116
|
"clear.prompt": "Clear all sub-agent records for this session? This cannot be undone.",
|
|
98
117
|
"clear.prompt_running": "{n} sub-agent(s) are still running. Clearing will discard them permanently. Continue?",
|
|
99
118
|
"clear.done": "Cleared {n} sub-agent record(s)",
|
|
119
|
+
"clear.failed": "Clear failed",
|
|
100
120
|
"clear.empty": "No sub-agent records in this session",
|
|
101
121
|
"cancel.no_session": "Child session ID is unavailable",
|
|
102
122
|
"cancel.not_child": "Target is not a child session",
|
|
@@ -108,6 +128,24 @@ const EN_T = {
|
|
|
108
128
|
"cancel.failed": "Cancellation failed",
|
|
109
129
|
"borderShown": "Panel border shown",
|
|
110
130
|
"borderHidden": "Panel border hidden",
|
|
131
|
+
"settings.title": "SubAgent Magazine — Settings",
|
|
132
|
+
"settings.section.general": "General",
|
|
133
|
+
"settings.lang": "Language",
|
|
134
|
+
"settings.max": "Max entries",
|
|
135
|
+
"settings.order": "Sort order",
|
|
136
|
+
"settings.scroll": "Scroll mode",
|
|
137
|
+
"settings.ttl": "Retention",
|
|
138
|
+
"settings.border": "Panel border",
|
|
139
|
+
"settings.showEntryCost": "Cost per sub-agent",
|
|
140
|
+
"settings.showEntryTime": "Elapsed time in list",
|
|
141
|
+
"settings.showEntryTokens": "Token count in list",
|
|
142
|
+
"settings.timeFormat": "Time format",
|
|
143
|
+
"settings.dbSync": "Local database enrichment",
|
|
144
|
+
"settings.on": "on",
|
|
145
|
+
"settings.off": "off",
|
|
146
|
+
"settings.saved": "Saved {item}: {value}",
|
|
147
|
+
"settings.invalid": "Invalid value",
|
|
148
|
+
"settings.close": "Close",
|
|
111
149
|
};
|
|
112
150
|
const JA_T = {
|
|
113
151
|
"panel.title": "サブエージェント",
|
|
@@ -150,6 +188,7 @@ const JA_T = {
|
|
|
150
188
|
"clear.prompt": "このセッションの全サブエージェント記録を削除しますか?この操作は元に戻せません。",
|
|
151
189
|
"clear.prompt_running": "実行中のサブエージェントが {n} 個あります。削除すると復元できません。続行しますか?",
|
|
152
190
|
"clear.done": "{n} 件のサブエージェント記録を削除しました",
|
|
191
|
+
"clear.failed": "クリアに失敗しました",
|
|
153
192
|
"clear.empty": "このセッションにサブエージェント記録はありません",
|
|
154
193
|
"cancel.no_session": "子セッション ID が利用できません",
|
|
155
194
|
"cancel.not_child": "対象は子セッションではありません",
|
|
@@ -161,6 +200,24 @@ const JA_T = {
|
|
|
161
200
|
"cancel.failed": "キャンセルに失敗しました",
|
|
162
201
|
"borderShown": "パネル枠線 表示",
|
|
163
202
|
"borderHidden": "パネル枠線 非表示",
|
|
203
|
+
"settings.title": "SubAgent Magazine — 設定",
|
|
204
|
+
"settings.section.general": "一般",
|
|
205
|
+
"settings.lang": "言語",
|
|
206
|
+
"settings.max": "最大表示数",
|
|
207
|
+
"settings.order": "並び順",
|
|
208
|
+
"settings.scroll": "スクロール",
|
|
209
|
+
"settings.ttl": "保持期間",
|
|
210
|
+
"settings.border": "パネル枠",
|
|
211
|
+
"settings.showEntryCost": "サブエージェント毎の費用",
|
|
212
|
+
"settings.showEntryTime": "一覧に所要時間を表示",
|
|
213
|
+
"settings.showEntryTokens": "一覧にトークン数を表示",
|
|
214
|
+
"settings.timeFormat": "時間形式",
|
|
215
|
+
"settings.dbSync": "ローカルDB補完",
|
|
216
|
+
"settings.on": "オン",
|
|
217
|
+
"settings.off": "オフ",
|
|
218
|
+
"settings.saved": "保存しました {item}:{value}",
|
|
219
|
+
"settings.invalid": "無効な値です",
|
|
220
|
+
"settings.close": "閉じる",
|
|
164
221
|
};
|
|
165
222
|
const KO_T = {
|
|
166
223
|
"panel.title": "서브 에이전트",
|
|
@@ -203,6 +260,7 @@ const KO_T = {
|
|
|
203
260
|
"clear.prompt": "이 세션의 모든 서브 에이전트 기록을 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
|
|
204
261
|
"clear.prompt_running": "실행 중인 서브 에이전트가 {n}개 있습니다. 삭제하면 복구할 수 없습니다. 계속하시겠습니까?",
|
|
205
262
|
"clear.done": "서브 에이전트 기록 {n}개를 삭제했습니다",
|
|
263
|
+
"clear.failed": "지우기에 실패했습니다",
|
|
206
264
|
"clear.empty": "이 세션에 서브 에이전트 기록이 없습니다",
|
|
207
265
|
"cancel.no_session": "하위 세션 ID를 사용할 수 없습니다",
|
|
208
266
|
"cancel.not_child": "대상이 하위 세션이 아닙니다",
|
|
@@ -214,6 +272,24 @@ const KO_T = {
|
|
|
214
272
|
"cancel.failed": "취소에 실패했습니다",
|
|
215
273
|
"borderShown": "패널 테두리 표시",
|
|
216
274
|
"borderHidden": "패널 테두리 숨김",
|
|
275
|
+
"settings.title": "SubAgent Magazine — 설정",
|
|
276
|
+
"settings.section.general": "일반",
|
|
277
|
+
"settings.lang": "언어",
|
|
278
|
+
"settings.max": "최대 항목 수",
|
|
279
|
+
"settings.order": "정렬 순서",
|
|
280
|
+
"settings.scroll": "스크롤 모드",
|
|
281
|
+
"settings.ttl": "보존 기간",
|
|
282
|
+
"settings.border": "패널 테두리",
|
|
283
|
+
"settings.showEntryCost": "하위 에이전트별 비용",
|
|
284
|
+
"settings.showEntryTime": "목록에 소요 시간 표시",
|
|
285
|
+
"settings.showEntryTokens": "목록에 토큰 수 표시",
|
|
286
|
+
"settings.timeFormat": "시간 형식",
|
|
287
|
+
"settings.dbSync": "로컬 DB 보완",
|
|
288
|
+
"settings.on": "켬",
|
|
289
|
+
"settings.off": "끔",
|
|
290
|
+
"settings.saved": "저장됨 {item}: {value}",
|
|
291
|
+
"settings.invalid": "잘못된 값",
|
|
292
|
+
"settings.close": "닫기",
|
|
217
293
|
};
|
|
218
294
|
export const LANGS = { zh: ZH_T, en: EN_T, ja: JA_T, ko: KO_T };
|
|
219
295
|
/** 语言元数据:/subagent-lang 选项与自动检测共用。 */
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createSignal } from "solid-js";
|
|
|
3
3
|
import { PLUGIN_VERSION } from "./_version";
|
|
4
4
|
import { LANG_META, createT, detectLang } from "./i18n";
|
|
5
5
|
import { KV_PREFIX } from "./core/kv";
|
|
6
|
+
import { TIME_FORMATS, TIME_FORMAT_SAMPLES } from "./core/format";
|
|
6
7
|
import { SubAgentPanel } from "./panel/SubAgentPanel";
|
|
7
8
|
import { globalEntryCache, setClearTick } from "./panel/store";
|
|
8
9
|
// Plugin entry
|
|
@@ -13,7 +14,7 @@ function createSidebarSlot(api, panelApi, sig) {
|
|
|
13
14
|
slots: {
|
|
14
15
|
sidebar_content(ctx, input) {
|
|
15
16
|
sig.sessionId = input.session_id;
|
|
16
|
-
return (_jsx(SubAgentPanel, { api: panelApi, theme: ctx.theme.current, lang: sig.lang, maxEntries: sig.maxEntries, sortOrder: sig.sortOrder, scrollMode: sig.scrollMode, borderVisible: sig.borderVisible, sessionId: input.session_id }));
|
|
17
|
+
return (_jsx(SubAgentPanel, { api: panelApi, theme: ctx.theme.current, lang: sig.lang, maxEntries: sig.maxEntries, sortOrder: sig.sortOrder, scrollMode: sig.scrollMode, borderVisible: sig.borderVisible, showEntryCost: sig.showEntryCost, showEntryTime: sig.showEntryTime, showEntryTokens: sig.showEntryTokens, timeFormat: sig.timeFormat, sessionId: input.session_id }));
|
|
17
18
|
},
|
|
18
19
|
},
|
|
19
20
|
};
|
|
@@ -27,7 +28,13 @@ const tui = async (api) => {
|
|
|
27
28
|
const [sortOrder, setSortOrder] = createSignal(String(api.kv.get(`${KV_PREFIX}.order`, "desc")) === "asc" ? "asc" : "desc");
|
|
28
29
|
const [scrollMode, setScrollMode] = createSignal(String(api.kv.get(`${KV_PREFIX}.scroll_mode`, "wheel")) === "click" ? "click" : "wheel");
|
|
29
30
|
const [borderVisible, setBorderVisible] = createSignal(api.kv.get(`${KV_PREFIX}.border`, false) === true);
|
|
30
|
-
const
|
|
31
|
+
const [showEntryCost, setShowEntryCost] = createSignal(api.kv.get(`${KV_PREFIX}.show_entry_cost`, false) === true);
|
|
32
|
+
const [showEntryTime, setShowEntryTime] = createSignal(api.kv.get(`${KV_PREFIX}.show_entry_time`, true) !== false);
|
|
33
|
+
const [showEntryTokens, setShowEntryTokens] = createSignal(api.kv.get(`${KV_PREFIX}.show_entry_tokens`, true) !== false);
|
|
34
|
+
const storedTimeFormat = String(api.kv.get(`${KV_PREFIX}.time_format`, "short"));
|
|
35
|
+
const [timeFormat, setTimeFormat] = createSignal(TIME_FORMATS.includes(storedTimeFormat) ? storedTimeFormat : "short");
|
|
36
|
+
const [dbSync, setDbSync] = createSignal(api.kv.get(`${KV_PREFIX}.db_sync`, true) !== false);
|
|
37
|
+
const signals = { lang, setLang, maxEntries, setMaxEntries, sortOrder, setSortOrder, scrollMode, setScrollMode, borderVisible, setBorderVisible, showEntryCost, setShowEntryCost, showEntryTime, setShowEntryTime, showEntryTokens, setShowEntryTokens, timeFormat, setTimeFormat, dbSync, setDbSync, sessionId: "" };
|
|
31
38
|
// ── V1 PanelApi adapter: wraps the V1 host API into the shared panel contract ──
|
|
32
39
|
const v1Api = {
|
|
33
40
|
kv: api.kv,
|
|
@@ -431,6 +438,70 @@ const tui = async (api) => {
|
|
|
431
438
|
dialog?.clear();
|
|
432
439
|
},
|
|
433
440
|
},
|
|
441
|
+
{
|
|
442
|
+
title: "SubAgent Magazine: Entry Cost",
|
|
443
|
+
value: "subagent-cost",
|
|
444
|
+
description: "Show or hide the per-sub-agent cost in the sidebar",
|
|
445
|
+
slash: { name: "subagent-cost" },
|
|
446
|
+
onSelect: (dialog) => {
|
|
447
|
+
const t = createT(() => signals.lang());
|
|
448
|
+
const cur = Boolean(api.kv.get(`${KV_PREFIX}.show_entry_cost`, false));
|
|
449
|
+
api.kv.set(`${KV_PREFIX}.show_entry_cost`, !cur);
|
|
450
|
+
signals.setShowEntryCost(!cur);
|
|
451
|
+
api.ui.toast({
|
|
452
|
+
message: `${t("settings.showEntryCost")}: ${!cur ? t("settings.on") : t("settings.off")}`,
|
|
453
|
+
});
|
|
454
|
+
dialog?.clear();
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
title: "SubAgent Magazine: Entry Time",
|
|
459
|
+
value: "subagent-time",
|
|
460
|
+
description: "Show or hide the elapsed time in the sidebar list",
|
|
461
|
+
slash: { name: "subagent-time" },
|
|
462
|
+
onSelect: (dialog) => {
|
|
463
|
+
const t = createT(() => signals.lang());
|
|
464
|
+
const cur = Boolean(api.kv.get(`${KV_PREFIX}.show_entry_time`, true));
|
|
465
|
+
api.kv.set(`${KV_PREFIX}.show_entry_time`, !cur);
|
|
466
|
+
signals.setShowEntryTime(!cur);
|
|
467
|
+
api.ui.toast({
|
|
468
|
+
message: `${t("settings.showEntryTime")}: ${!cur ? t("settings.on") : t("settings.off")}`,
|
|
469
|
+
});
|
|
470
|
+
dialog?.clear();
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
title: "SubAgent Magazine: Entry Tokens",
|
|
475
|
+
value: "subagent-tokens",
|
|
476
|
+
description: "Show or hide the token count in the sidebar list",
|
|
477
|
+
slash: { name: "subagent-tokens" },
|
|
478
|
+
onSelect: (dialog) => {
|
|
479
|
+
const t = createT(() => signals.lang());
|
|
480
|
+
const cur = Boolean(api.kv.get(`${KV_PREFIX}.show_entry_tokens`, true));
|
|
481
|
+
api.kv.set(`${KV_PREFIX}.show_entry_tokens`, !cur);
|
|
482
|
+
signals.setShowEntryTokens(!cur);
|
|
483
|
+
api.ui.toast({
|
|
484
|
+
message: `${t("settings.showEntryTokens")}: ${!cur ? t("settings.on") : t("settings.off")}`,
|
|
485
|
+
});
|
|
486
|
+
dialog?.clear();
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
title: "SubAgent Magazine: Time Format",
|
|
491
|
+
value: "subagent-time-format",
|
|
492
|
+
description: "Set how elapsed time is displayed",
|
|
493
|
+
slash: { name: "subagent-time-format" },
|
|
494
|
+
onSelect: (dialog) => {
|
|
495
|
+
dialog?.replace(() => (_jsx(api.ui.DialogSelect, { title: "Time Format / \u65F6\u95F4\u683C\u5F0F", options: TIME_FORMATS.map((f) => ({ title: `${f} — ${TIME_FORMAT_SAMPLES[f]}`, value: f })), onSelect: (opt) => {
|
|
496
|
+
const f = opt.value;
|
|
497
|
+
setTimeFormat(f);
|
|
498
|
+
api.kv.set(`${KV_PREFIX}.time_format`, f);
|
|
499
|
+
const t = createT(() => lang());
|
|
500
|
+
api.ui.toast({ message: `${t("settings.timeFormat")}: ${TIME_FORMAT_SAMPLES[f]}` });
|
|
501
|
+
dialog?.clear();
|
|
502
|
+
} })));
|
|
503
|
+
},
|
|
504
|
+
},
|
|
434
505
|
]);
|
|
435
506
|
};
|
|
436
507
|
const mod = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @jsxImportSource @opentui/solid */
|
|
2
2
|
import type { JSX } from "@opentui/solid";
|
|
3
|
-
import type { Lang, SortOrder, ScrollMode } from "../core/types";
|
|
3
|
+
import type { Lang, SortOrder, ScrollMode, TimeFormat } from "../core/types";
|
|
4
4
|
import type { PanelApi } from "./panel-api";
|
|
5
5
|
export declare function SubAgentPanel(props: {
|
|
6
6
|
api: PanelApi;
|
|
@@ -10,5 +10,9 @@ export declare function SubAgentPanel(props: {
|
|
|
10
10
|
sortOrder: () => SortOrder;
|
|
11
11
|
scrollMode: () => ScrollMode;
|
|
12
12
|
borderVisible: () => boolean;
|
|
13
|
+
showEntryCost: () => boolean;
|
|
14
|
+
showEntryTime: () => boolean;
|
|
15
|
+
showEntryTokens: () => boolean;
|
|
16
|
+
timeFormat: () => TimeFormat;
|
|
13
17
|
sessionId: string;
|
|
14
18
|
}): JSX.Element;
|