zcode-pro 1.5.0__tar.gz → 1.5.1__tar.gz
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.
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/PKG-INFO +2 -2
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/README.en.md +1 -1
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/README.md +1 -1
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/dist/inject.js +113 -47
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/package.json +1 -1
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/helper.mjs +38 -7
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/taskIndex.mjs +11 -7
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/core.js +6 -10
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/newtab-panels.js +3 -1
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/session-switch.js +92 -30
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/settings-dialog.js +1 -2
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/title-gen.js +3 -2
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/title-lock.js +3 -2
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/ws-running.js +55 -8
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/zcode_pro/__init__.py +1 -1
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/.gitignore +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/LICENSE +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/bin/zcode-pro +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/bin/zcode-pro.cmd +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/bin/zcode-pro.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/bin/zcode-pro.vbs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/cli.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/pyproject.toml +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/cdp.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/main.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/paths.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/pickFolder.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/settings.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/setup.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/taskOrder.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/version.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/host/webdav.mjs +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/alias-migration.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/alias.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/composer-provider.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/copy-path.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/file-menu.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/idle-reclaim.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/image-menu.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/open-folder.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/pinned-collapse.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/plugin-card-retry.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/plugin-updates.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/project-menu.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/relocate-dialog.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/sandbox-reclaim.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/settings-entry.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/status-bar.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/styles.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/table-copy.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/task-order.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/theme.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/toolbar-icons.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/turn-usage.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/features/ws-rows.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/index.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/provider-fields.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/src/inject/ui.js +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/zcode_pro/__main__.py +0 -0
- {zcode_pro-1.5.0 → zcode_pro-1.5.1}/zcode_pro/cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: zcode-pro
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.1
|
|
4
4
|
Summary: ZCode Pro — ZCode 桌面版界面增强启动器(不修改客户端文件),核心功能:项目自定义别名
|
|
5
5
|
Project-URL: Homepage, https://github.com/duanluan/zcode-pro
|
|
6
6
|
Project-URL: Repository, https://github.com/duanluan/zcode-pro
|
|
@@ -38,7 +38,7 @@ ZCode 桌面版界面增强工具。不修改客户端文件,通过 ZCode Pro
|
|
|
38
38
|
| | ~~项目自定义别名~~(已停用) | 侧边栏显示自定义名称,仅界面显示,随时恢复;ZCode ≥ 3.16.1 已内置改名(项目菜单“编辑项目”),本功能自动停用并引导把已有别名迁移过去 |
|
|
39
39
|
| | ~~切换文件夹~~(已停用) | 将项目指向另一个文件夹,侧边栏、标签页与任务历史一并迁移;切换前先停止运行中的任务;历史检查点仍引用原路径;ZCode ≥ 3.16.1 官方已支持一个项目挂多个目录,本功能随之停用 |
|
|
40
40
|
| | 会话拖动排序 | 拖动调整置顶、项目与分组中的会话顺序,重启后不变 |
|
|
41
|
-
| | 会话快捷切换 | `alt+z` 在当前与上次会话间来回切换;按住 `alt` 按 `x`(上移)/ `c`(下移)在最近会话列表中选择(初始 10 条,`c` 到底再加载 5 条),`alt+e` 直接打开最近 10 条,`alt+r`
|
|
41
|
+
| | 会话快捷切换 | `alt+z` 在当前与上次会话间来回切换;按住 `alt` 按 `x`(上移)/ `c`(下移)在最近会话列表中选择(初始 10 条,`c` 到底再加载 5 条),`alt+e` 直接打开最近 10 条,`alt+r` 打开正在运行与运行完未查看的任务;方向键同样可选,`Enter` 或点击行切换;五个入口键可在 设置 → 快捷键 中修改 |
|
|
42
42
|
| | 重命名会话名锁定 | 手动重命名过的会话不再被自动标题覆盖,侧栏始终显示重命名后的名字 |
|
|
43
43
|
| | 已置顶分区可折叠 | 侧边栏“已置顶”标题可点击折叠/展开其任务列表 |
|
|
44
44
|
| | 文件菜单增强 | 会话中文件链接的右键菜单新增“默认应用打开”“打开所在目录” |
|
|
@@ -18,7 +18,7 @@ Grouped by where each feature lives in the settings window: the category column
|
|
|
18
18
|
| | ~~Custom project alias~~ (retired) | Show a custom name for a project in the sidebar; UI-only, revert anytime; renaming is built into ZCode ≥ 3.16.1 (project menu "Edit project") — the feature disables itself there and offers to migrate existing aliases |
|
|
19
19
|
| | ~~Switch project folder~~ (retired) | Point a project at another folder; sidebar, tabs and task history follow. Stop running tasks before switching; historical checkpoints still reference the original path; retired on ZCode ≥ 3.16.1, where a project can hold multiple directories natively |
|
|
20
20
|
| | Session drag ordering | Reorder sessions in Pinned, Projects and Groups; persists across refreshes |
|
|
21
|
-
| | Session quick switch | `alt+z` toggles between the current and the last session; hold `alt` and press `x` (up) / `c` (down) to pick from recent sessions (10 shown initially, pressing `c` at the bottom loads 5 more), `alt+e` opens the last 10 directly, `alt+r` opens running / finished-but-unseen tasks; arrow keys also move the highlight,
|
|
21
|
+
| | Session quick switch | `alt+z` toggles between the current and the last session; hold `alt` and press `x` (up) / `c` (down) to pick from recent sessions (10 shown initially, pressing `c` at the bottom loads 5 more), `alt+e` opens the last 10 directly, `alt+r` opens running / finished-but-unseen tasks; arrow keys also move the highlight, press `Enter` or click a row to switch; the five trigger keys are customizable in Settings → Shortcuts |
|
|
22
22
|
| | Keep renamed session titles | Sessions you renamed keep their custom title; auto-generated titles no longer overwrite it in the sidebar |
|
|
23
23
|
| | Collapsible Pinned section | The sidebar "Pinned" header collapses/expands its task list; the state is remembered |
|
|
24
24
|
| | File menu actions | Adds "Open with default app" and "Reveal in file manager" to file links in chat |
|
|
@@ -18,7 +18,7 @@ ZCode 桌面版界面增强工具。不修改客户端文件,通过 ZCode Pro
|
|
|
18
18
|
| | ~~项目自定义别名~~(已停用) | 侧边栏显示自定义名称,仅界面显示,随时恢复;ZCode ≥ 3.16.1 已内置改名(项目菜单“编辑项目”),本功能自动停用并引导把已有别名迁移过去 |
|
|
19
19
|
| | ~~切换文件夹~~(已停用) | 将项目指向另一个文件夹,侧边栏、标签页与任务历史一并迁移;切换前先停止运行中的任务;历史检查点仍引用原路径;ZCode ≥ 3.16.1 官方已支持一个项目挂多个目录,本功能随之停用 |
|
|
20
20
|
| | 会话拖动排序 | 拖动调整置顶、项目与分组中的会话顺序,重启后不变 |
|
|
21
|
-
| | 会话快捷切换 | `alt+z` 在当前与上次会话间来回切换;按住 `alt` 按 `x`(上移)/ `c`(下移)在最近会话列表中选择(初始 10 条,`c` 到底再加载 5 条),`alt+e` 直接打开最近 10 条,`alt+r`
|
|
21
|
+
| | 会话快捷切换 | `alt+z` 在当前与上次会话间来回切换;按住 `alt` 按 `x`(上移)/ `c`(下移)在最近会话列表中选择(初始 10 条,`c` 到底再加载 5 条),`alt+e` 直接打开最近 10 条,`alt+r` 打开正在运行与运行完未查看的任务;方向键同样可选,`Enter` 或点击行切换;五个入口键可在 设置 → 快捷键 中修改 |
|
|
22
22
|
| | 重命名会话名锁定 | 手动重命名过的会话不再被自动标题覆盖,侧栏始终显示重命名后的名字 |
|
|
23
23
|
| | 已置顶分区可折叠 | 侧边栏“已置顶”标题可点击折叠/展开其任务列表 |
|
|
24
24
|
| | 文件菜单增强 | 会话中文件链接的右键菜单新增“默认应用打开”“打开所在目录” |
|
|
@@ -280,14 +280,14 @@
|
|
|
280
280
|
userFontDesc: "\u4F1A\u8BDD\u4E2D\u63D0\u95EE\u5185\u5BB9\u7684\u5B57\u4F53\uFF1B\u9009\u201C\u9ED8\u8BA4\u201D\u65F6\u8DDF\u968F\u754C\u9762\u5B57\u4F53\u3002",
|
|
281
281
|
assistantFontName: "\u56DE\u7B54\u5B57\u4F53",
|
|
282
282
|
assistantFontDesc: "\u4F1A\u8BDD\u4E2D\u56DE\u7B54\u6B63\u6587\u7684\u5B57\u4F53\uFF08\u4EE3\u7801\u5757\u4ECD\u7528\u7B49\u5BBD\u5B57\u4F53\uFF09\uFF1B\u9009\u201C\u9ED8\u8BA4\u201D\u65F6\u8DDF\u968F\u754C\u9762\u5B57\u4F53\u3002",
|
|
283
|
-
switcherHint: "{prev} / {next} \u6216 \u2191 / \u2193 \u9009\u62E9\uFF0CEnter \u6216\
|
|
283
|
+
switcherHint: "{prev} / {next} \u6216 \u2191 / \u2193 \u9009\u62E9\uFF0CEnter \u6216\u70B9\u51FB\u884C\u5207\u6362\uFF0CEsc \u53D6\u6D88",
|
|
284
284
|
switcherCurrent: "\u5F53\u524D",
|
|
285
285
|
switcherEmpty: "\u6682\u65E0\u4E0A\u6B21\u4F1A\u8BDD\uFF08\u5207\u6362\u8FC7\u4F1A\u8BDD\u540E\u53EF\u7528\uFF09",
|
|
286
286
|
switcherFailed: "\u672A\u80FD\u5207\u6362\u5230\u8BE5\u4F1A\u8BDD\uFF08\u53EF\u80FD\u5DF2\u5220\u9664\uFF09",
|
|
287
287
|
switcherMore: "\u8FD8\u6709 {n} \u6761 \xB7 \u6309 c \u52A0\u8F7D",
|
|
288
288
|
switcherRunning: "\u8FD0\u884C\u4E2D",
|
|
289
289
|
switcherUnseen: "\u672A\u67E5\u770B",
|
|
290
|
-
switcherAttentionHint: "\u8FD0\u884C\u4E2D / \u8FD0\u884C\u5B8C\u672A\u67E5\u770B\u7684\u4EFB\u52A1 \xB7 {prev} / {next} \u6216 \u2191 / \u2193 \u9009\u62E9\uFF0CEnter \u6216\
|
|
290
|
+
switcherAttentionHint: "\u8FD0\u884C\u4E2D / \u8FD0\u884C\u5B8C\u672A\u67E5\u770B\u7684\u4EFB\u52A1 \xB7 {prev} / {next} \u6216 \u2191 / \u2193 \u9009\u62E9\uFF0CEnter \u6216\u70B9\u51FB\u884C\u5207\u6362",
|
|
291
291
|
switcherAttentionEmpty: "\u6682\u65E0\u8FD0\u884C\u4E2D\u6216\u672A\u67E5\u770B\u7684\u4EFB\u52A1",
|
|
292
292
|
featureNewtabPanels: "\u65B0\u6807\u7B7E\u9875\u63D2\u4EF6\u9762\u677F\u5165\u53E3",
|
|
293
293
|
featureNewtabPanelsDesc: "\u65B0\u6807\u7B7E\u9875\u52A0\u5165\u7B2C\u4E09\u65B9\u63D2\u4EF6\uFF08headroom\u3001rtk \u7B49\uFF09\u7684\u9762\u677F\u5361\u7247\uFF0C\u70B9\u51FB\u5728\u4FA7\u8FB9\u9762\u677F\u6253\u5F00\u3002",
|
|
@@ -384,8 +384,6 @@
|
|
|
384
384
|
hkArrow: "\u5F39\u7A97\u6253\u5F00\u65F6\u79FB\u52A8\u9AD8\u4EAE\uFF08\u4E0E alt + {prev} / alt + {next} \u540C\u89C4\u5219\uFF09",
|
|
385
385
|
hkEnter: "\u786E\u8BA4\u5207\u6362\u5230\u9AD8\u4EAE\u7684\u4F1A\u8BDD",
|
|
386
386
|
hkEsc: "\u53D6\u6D88\u5E76\u5173\u95ED\u5F39\u7A97",
|
|
387
|
-
hkRelease: "\u79FB\u52A8\u8FC7\u9AD8\u4EAE\u65F6\u677E\u5F00\u5373\u5207\u6362\uFF1B\u4ECE\u672A\u79FB\u52A8\u5219\u5F39\u7A97\u4FDD\u6301\u6253\u5F00\uFF0C\u7EE7\u7EED\u9009\u62E9",
|
|
388
|
-
hkReleaseKey: "\u677E\u5F00 alt",
|
|
389
387
|
hkModify: "\u4FEE\u6539",
|
|
390
388
|
hkRecording: "\u6309\u4E0B\u65B0\u952E\u2026",
|
|
391
389
|
hkInvalid: "\u5FEB\u6377\u952E\u53EA\u652F\u6301\u5355\u4E2A\u5B57\u6BCD\u6216\u6570\u5B57",
|
|
@@ -478,7 +476,7 @@
|
|
|
478
476
|
headroomDesc: "Headroom \u63D2\u4EF6\uFF1A\u672C\u5730\u538B\u7F29\u4EE3\u7406\u9A7B\u7559 127.0.0.1\uFF08\u9ED8\u8BA4 8787 \u7AEF\u53E3\uFF09\uFF0C\u538B\u7F29\u53D1\u5F80\u6A21\u578B\u7684\u4E0A\u4E0B\u6587\u4EE5\u7701 token\uFF08/hr-* \u547D\u4EE4\u7F16\u8F91\u540C\u4E00\u914D\u7F6E\uFF09\u3002\u5207\u6362\u8BBE\u5907\u4F1A\u91CD\u542F\u4EE3\u7406\uFF0C\u6B63\u5728\u5904\u7406\u7684\u8BF7\u6C42\u53EF\u80FD\u4E2D\u65AD\u4E00\u6B21\u3002",
|
|
479
477
|
headroomBinMissing: "\u672A\u627E\u5230 headroom \u7A0B\u5E8F\uFF0C\u4EE3\u7406\u65E0\u6CD5\u542F\u52A8\uFF1B\u5B89\u88C5 headroom \u540E\u4EE5\u4E0B\u8BBE\u7F6E\u81EA\u52A8\u751F\u6548",
|
|
480
478
|
hrInstallBin: "\u5B89\u88C5 headroom \u672C\u4F53",
|
|
481
|
-
hrBinInstalled: "headroom \u672C\u4F53\u5DF2\u5B89\u88C5\uFF1B\u91CD\u65B0\u6253\u5F00\u4F1A\u8BDD\u540E\u4EE3\u7406\u53EF\u7528\uFF08SessionStart \u94A9\u5B50\u81EA\u52A8\u62C9\u8D77\uFF09",
|
|
479
|
+
hrBinInstalled: "headroom \u672C\u4F53\u5DF2\u5B89\u88C5\uFF1B\u91CD\u65B0\u6253\u5F00\u5E94\u7528\u6216\u4F1A\u8BDD\u540E\u4EE3\u7406\u53EF\u7528\uFF08SessionStart \u94A9\u5B50\u81EA\u52A8\u62C9\u8D77\uFF09",
|
|
482
480
|
headroomInstallTitle: "\u672A\u5B89\u88C5 headroom \u63D2\u4EF6",
|
|
483
481
|
headroomInstallDesc: "\u4ECE zcode-plugins \u5E02\u573A\u5B89\u88C5\u540E\u5373\u53EF\u5728\u6B64\u7BA1\u7406\u538B\u7F29\u4EE3\u7406\uFF1B\u91CD\u542F\u4F1A\u8BDD\u540E\u751F\u6548\u3002",
|
|
484
482
|
headroomInstall: "\u5B89\u88C5",
|
|
@@ -792,14 +790,14 @@
|
|
|
792
790
|
userFontDesc: 'Font of question text in conversations; "default" follows the UI font.',
|
|
793
791
|
assistantFontName: "Answer font",
|
|
794
792
|
assistantFontDesc: 'Font of answer text in conversations (code blocks stay monospaced); "default" follows the UI font.',
|
|
795
|
-
switcherHint: "{prev} / {next} or \u2191 / \u2193 to choose, Enter or
|
|
793
|
+
switcherHint: "{prev} / {next} or \u2191 / \u2193 to choose, Enter or click a row to switch, Esc to cancel",
|
|
796
794
|
switcherCurrent: "current",
|
|
797
795
|
switcherEmpty: "No previous session yet (available after you switch sessions)",
|
|
798
796
|
switcherFailed: "Failed to switch to that session (it may have been deleted)",
|
|
799
797
|
switcherMore: "{n} more \xB7 press c to load",
|
|
800
798
|
switcherRunning: "running",
|
|
801
799
|
switcherUnseen: "unseen",
|
|
802
|
-
switcherAttentionHint: "Running / finished but unseen tasks \xB7 {prev} / {next} or \u2191 / \u2193 to choose, Enter or
|
|
800
|
+
switcherAttentionHint: "Running / finished but unseen tasks \xB7 {prev} / {next} or \u2191 / \u2193 to choose, Enter or click a row to switch",
|
|
803
801
|
switcherAttentionEmpty: "No running or unseen tasks",
|
|
804
802
|
featureNewtabPanels: "Plugin panels on the new tab page",
|
|
805
803
|
featureNewtabPanelsDesc: "Adds third-party plugin panels (headroom, rtk, ...) to the new tab page; click to open in the side pane.",
|
|
@@ -896,8 +894,6 @@
|
|
|
896
894
|
hkArrow: "Move the highlight while the popup is open (same rules as alt + {prev} / alt + {next})",
|
|
897
895
|
hkEnter: "Switch to the highlighted session",
|
|
898
896
|
hkEsc: "Cancel and close the popup",
|
|
899
|
-
hkRelease: "Releasing alt switches once the highlight has moved; otherwise the popup stays open for further selection",
|
|
900
|
-
hkReleaseKey: "release alt",
|
|
901
897
|
hkModify: "Change",
|
|
902
898
|
hkRecording: "press a key\u2026",
|
|
903
899
|
hkInvalid: "Shortcuts accept a single letter or digit only",
|
|
@@ -990,7 +986,7 @@
|
|
|
990
986
|
headroomDesc: "Headroom plugin: a local compression proxy on 127.0.0.1 (port 8787 by default) that compresses context sent to the model to save tokens (/hr-* commands edit the same file). Switching the device restarts the proxy, so requests in progress may be interrupted once.",
|
|
991
987
|
headroomBinMissing: "The headroom program was not found, so the proxy cannot start. The settings below take effect once it is installed.",
|
|
992
988
|
hrInstallBin: "Install headroom binary",
|
|
993
|
-
hrBinInstalled: "headroom installed; reopen a session and the
|
|
989
|
+
hrBinInstalled: "headroom installed; reopen the app or a session and the proxy will start automatically",
|
|
994
990
|
headroomInstallTitle: "headroom plugin not installed",
|
|
995
991
|
headroomInstallDesc: "Install it from the zcode-plugins marketplace to manage the compression proxy here; takes effect after a session restart.",
|
|
996
992
|
headroomInstall: "Install",
|
|
@@ -4923,8 +4919,7 @@
|
|
|
4923
4919
|
}),
|
|
4924
4920
|
hkCell("\u2191 / \u2193", L.hkArrow.replaceAll("{prev}", keys.switchPrev).replaceAll("{next}", keys.switchNext)),
|
|
4925
4921
|
hkCell("Enter", L.hkEnter),
|
|
4926
|
-
hkCell("Esc", L.hkEsc)
|
|
4927
|
-
hkCell(L.hkReleaseKey, L.hkRelease)
|
|
4922
|
+
hkCell("Esc", L.hkEsc)
|
|
4928
4923
|
);
|
|
4929
4924
|
};
|
|
4930
4925
|
const stopHkRecording = () => {
|
|
@@ -8044,7 +8039,7 @@
|
|
|
8044
8039
|
}
|
|
8045
8040
|
}
|
|
8046
8041
|
function workspaceOf(key) {
|
|
8047
|
-
const i = key.
|
|
8042
|
+
const i = key.lastIndexOf(":");
|
|
8048
8043
|
return i > 0 ? key.slice(0, i) : key;
|
|
8049
8044
|
}
|
|
8050
8045
|
function wsHead(row) {
|
|
@@ -8182,6 +8177,7 @@
|
|
|
8182
8177
|
var anyActiveCache = { at: 0, value: null, busy: false };
|
|
8183
8178
|
var sectionActive = { task: false, project: false, pinned: false };
|
|
8184
8179
|
var activeProjectPaths = /* @__PURE__ */ new Set();
|
|
8180
|
+
var authActiveByWs = /* @__PURE__ */ new Map();
|
|
8185
8181
|
setInterval(() => {
|
|
8186
8182
|
if (!anyActiveCache.busy && Date.now() - anyActiveCache.at >= 1e4) globalAnyActive();
|
|
8187
8183
|
}, 1e4);
|
|
@@ -8193,6 +8189,7 @@
|
|
|
8193
8189
|
sectionActive.project = false;
|
|
8194
8190
|
sectionActive.pinned = false;
|
|
8195
8191
|
activeProjectPaths = /* @__PURE__ */ new Set();
|
|
8192
|
+
authActiveByWs = /* @__PURE__ */ new Map();
|
|
8196
8193
|
void (async () => {
|
|
8197
8194
|
let hit = null;
|
|
8198
8195
|
try {
|
|
@@ -8244,12 +8241,24 @@
|
|
|
8244
8241
|
const allIds = [...byKind.task, ...byKind.project];
|
|
8245
8242
|
const st = allIds.length ? await rpc("/task-statuses?ids=" + encodeURIComponent(allIds.join(","))).catch(() => null) : null;
|
|
8246
8243
|
const TERMINAL = /* @__PURE__ */ new Set(["completed", "error", "interrupted"]);
|
|
8244
|
+
const recOf = (id) => st && st.ok && st.available ? st.statuses[id] : void 0;
|
|
8247
8245
|
const live = (id) => {
|
|
8248
|
-
const rec =
|
|
8246
|
+
const rec = recOf(id);
|
|
8249
8247
|
if (rec === void 0) return true;
|
|
8250
8248
|
if (!TERMINAL.has(rec.status)) return true;
|
|
8251
8249
|
return Date.now() - rec.updatedAt < 3e5;
|
|
8252
8250
|
};
|
|
8251
|
+
const liveSession = (id) => {
|
|
8252
|
+
const rec = recOf(id);
|
|
8253
|
+
if (rec === void 0) return !!domRowKeyOf(id);
|
|
8254
|
+
if (!TERMINAL.has(rec.status)) return true;
|
|
8255
|
+
return Date.now() - rec.updatedAt < 3e5;
|
|
8256
|
+
};
|
|
8257
|
+
authActiveByWs = /* @__PURE__ */ new Map();
|
|
8258
|
+
for (const [base, ids] of byKindByPath) {
|
|
8259
|
+
const liveIds = ids.filter(liveSession);
|
|
8260
|
+
if (liveIds.length) authActiveByWs.set(base, liveIds);
|
|
8261
|
+
}
|
|
8253
8262
|
activeProjectPaths = /* @__PURE__ */ new Set();
|
|
8254
8263
|
for (const [base, ids] of byKindByPath) {
|
|
8255
8264
|
if (!projPaths.has(base)) continue;
|
|
@@ -8272,7 +8281,7 @@
|
|
|
8272
8281
|
for (const el of list.querySelectorAll("[data-task-item-key]")) {
|
|
8273
8282
|
if (el.closest("section[data-project-id]")) continue;
|
|
8274
8283
|
const k = el.getAttribute("data-task-item-key") || "";
|
|
8275
|
-
const id = k.slice(k.
|
|
8284
|
+
const id = k.slice(k.lastIndexOf(":") + 1);
|
|
8276
8285
|
const base = k.slice(0, k.lastIndexOf(":")).replace(/[\/]+$/, "");
|
|
8277
8286
|
const ids = byKindByPath.get(base) || [];
|
|
8278
8287
|
if (ids.map(String).includes(id) && ids.some(live)) {
|
|
@@ -8423,9 +8432,23 @@
|
|
|
8423
8432
|
current: curKey ? workspaceOf(curKey) || null : null
|
|
8424
8433
|
};
|
|
8425
8434
|
}
|
|
8435
|
+
function domRowKeyOf(id) {
|
|
8436
|
+
if (!id) return "";
|
|
8437
|
+
try {
|
|
8438
|
+
const el = document.querySelector(`[data-task-item-key$=":${CSS.escape(id)}"]`);
|
|
8439
|
+
return el && el.getAttribute("data-task-item-key") || "";
|
|
8440
|
+
} catch {
|
|
8441
|
+
return "";
|
|
8442
|
+
}
|
|
8443
|
+
}
|
|
8426
8444
|
function wsRunningKeys() {
|
|
8427
8445
|
const keys = /* @__PURE__ */ new Set();
|
|
8428
|
-
for (const set of runningByWs.values())
|
|
8446
|
+
for (const set of runningByWs.values()) {
|
|
8447
|
+
for (const k of set) if (!k.endsWith(":active")) keys.add(k);
|
|
8448
|
+
}
|
|
8449
|
+
for (const [base, ids] of authActiveByWs) {
|
|
8450
|
+
for (const id of ids) keys.add(domRowKeyOf(id) || `${base}:${id}`);
|
|
8451
|
+
}
|
|
8429
8452
|
return keys;
|
|
8430
8453
|
}
|
|
8431
8454
|
async function verifyWorkspaceNotRunning(ws) {
|
|
@@ -8603,7 +8626,6 @@
|
|
|
8603
8626
|
var visibleCount = INITIAL_COUNT;
|
|
8604
8627
|
var unseen = /* @__PURE__ */ new Set();
|
|
8605
8628
|
var lastRunKeys = null;
|
|
8606
|
-
var movedSinceOpen = false;
|
|
8607
8629
|
var hotkeys = { ...DEFAULT_SHORTCUTS };
|
|
8608
8630
|
function loadHotkeys() {
|
|
8609
8631
|
return getConfig(true).then((cfg) => {
|
|
@@ -8621,7 +8643,7 @@
|
|
|
8621
8643
|
return (tEl ? tEl.textContent : (item.innerText || "").split("\n")[0])?.trim() || "";
|
|
8622
8644
|
}
|
|
8623
8645
|
function workspaceOf2(key) {
|
|
8624
|
-
const i = key.
|
|
8646
|
+
const i = key.lastIndexOf(":");
|
|
8625
8647
|
return i > 0 ? key.slice(0, i) : key;
|
|
8626
8648
|
}
|
|
8627
8649
|
function findCurrent() {
|
|
@@ -8672,7 +8694,8 @@
|
|
|
8672
8694
|
}, SCAN_DEBOUNCE_MS2);
|
|
8673
8695
|
}
|
|
8674
8696
|
async function switchTo(key) {
|
|
8675
|
-
const
|
|
8697
|
+
const id = key.slice(key.lastIndexOf(":") + 1);
|
|
8698
|
+
const find = () => document.querySelector(`[data-task-item-key="${CSS.escape(key)}"]`) || (id ? document.querySelector(`[data-task-item-key$=":${CSS.escape(id)}"]`) : null);
|
|
8676
8699
|
let item = find();
|
|
8677
8700
|
if (!item) {
|
|
8678
8701
|
const ws = workspaceOf2(key);
|
|
@@ -8690,23 +8713,28 @@
|
|
|
8690
8713
|
}
|
|
8691
8714
|
function attentionList() {
|
|
8692
8715
|
const run = wsRunningKeys();
|
|
8716
|
+
const idOf = (k) => k.slice(k.lastIndexOf(":") + 1);
|
|
8717
|
+
const runKeyById = new Map([...run].map((k) => [idOf(k), k]));
|
|
8693
8718
|
const listed = /* @__PURE__ */ new Set();
|
|
8694
8719
|
const head = [];
|
|
8695
8720
|
for (const k of mru) {
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8721
|
+
const id = idOf(k);
|
|
8722
|
+
if (listed.has(id)) continue;
|
|
8723
|
+
const runForm = runKeyById.get(id);
|
|
8724
|
+
if (run.has(k) || unseen.has(k) || runForm) {
|
|
8725
|
+
head.push(runForm || k);
|
|
8726
|
+
listed.add(id);
|
|
8699
8727
|
}
|
|
8700
8728
|
}
|
|
8701
8729
|
const tail = [];
|
|
8702
|
-
|
|
8730
|
+
const push = (k) => {
|
|
8731
|
+
const id = idOf(k);
|
|
8732
|
+
if (listed.has(id)) return;
|
|
8733
|
+
listed.add(id);
|
|
8703
8734
|
tail.push(k);
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
for (const k of unseen)
|
|
8707
|
-
tail.push(k);
|
|
8708
|
-
listed.add(k);
|
|
8709
|
-
}
|
|
8735
|
+
};
|
|
8736
|
+
for (const k of run) push(k);
|
|
8737
|
+
for (const k of unseen) push(k);
|
|
8710
8738
|
return [...head, ...tail];
|
|
8711
8739
|
}
|
|
8712
8740
|
function activeList() {
|
|
@@ -8727,9 +8755,23 @@
|
|
|
8727
8755
|
function findTitle(key) {
|
|
8728
8756
|
const item = document.querySelector(`[data-task-item-key="${CSS.escape(key)}"]`);
|
|
8729
8757
|
if (item) titles.set(key, firstLineOf(item));
|
|
8730
|
-
const id = key.slice(key.
|
|
8758
|
+
const id = key.slice(key.lastIndexOf(":") + 1);
|
|
8731
8759
|
return titles.get(key) || `sess ${id.slice(0, 8)}`;
|
|
8732
8760
|
}
|
|
8761
|
+
async function fetchMissingTitles(keys) {
|
|
8762
|
+
const sessIdOf3 = (k) => k.slice(k.lastIndexOf(":") + 1);
|
|
8763
|
+
const missing = keys.filter((k) => {
|
|
8764
|
+
const id = sessIdOf3(k);
|
|
8765
|
+
return id && !titles.get(k) && !document.querySelector(`[data-task-item-key$=":${CSS.escape(id)}"]`);
|
|
8766
|
+
});
|
|
8767
|
+
if (!missing.length) return;
|
|
8768
|
+
const st = await rpc("/task-statuses?ids=" + encodeURIComponent(missing.map(sessIdOf3).join(","))).catch(() => null);
|
|
8769
|
+
if (!st || !st.ok || !st.available || !st.statuses) return;
|
|
8770
|
+
for (const k of missing) {
|
|
8771
|
+
const rec = st.statuses[sessIdOf3(k)];
|
|
8772
|
+
if (rec && rec.title) titles.set(k, String(rec.title));
|
|
8773
|
+
}
|
|
8774
|
+
}
|
|
8733
8775
|
function projLabelOf(key, aliases2) {
|
|
8734
8776
|
const ws = workspaceOf2(key);
|
|
8735
8777
|
if (!ws) return "";
|
|
@@ -8775,14 +8817,23 @@
|
|
|
8775
8817
|
));
|
|
8776
8818
|
}
|
|
8777
8819
|
}
|
|
8778
|
-
|
|
8820
|
+
var openPopupInflight = null;
|
|
8821
|
+
function openPopup(mode = "mru") {
|
|
8779
8822
|
if (popupEl) return;
|
|
8823
|
+
if (openPopupInflight) return openPopupInflight;
|
|
8824
|
+
openPopupInflight = openPopupInner(mode).finally(() => {
|
|
8825
|
+
openPopupInflight = null;
|
|
8826
|
+
});
|
|
8827
|
+
return openPopupInflight;
|
|
8828
|
+
}
|
|
8829
|
+
async function openPopupInner(mode) {
|
|
8780
8830
|
popupMode = mode;
|
|
8781
8831
|
visibleCount = INITIAL_COUNT;
|
|
8782
8832
|
highlight = 0;
|
|
8783
|
-
movedSinceOpen = false;
|
|
8784
8833
|
const cfg = await getConfig().catch(() => null);
|
|
8785
8834
|
aliasesCache = cfg && cfg.aliases && typeof cfg.aliases === "object" ? cfg.aliases : {};
|
|
8835
|
+
if (mode === "attention") await fetchMissingTitles(attentionList()).catch(() => {
|
|
8836
|
+
});
|
|
8786
8837
|
ensureStyle();
|
|
8787
8838
|
const L = t();
|
|
8788
8839
|
popupEl = h(
|
|
@@ -8817,7 +8868,6 @@
|
|
|
8817
8868
|
const len = Math.min(activeList().length, visibleCount);
|
|
8818
8869
|
if (len < 2) return;
|
|
8819
8870
|
highlight = highlight > 0 ? highlight - 1 : len - 1;
|
|
8820
|
-
movedSinceOpen = true;
|
|
8821
8871
|
renderPopup();
|
|
8822
8872
|
scrollHighlight();
|
|
8823
8873
|
}
|
|
@@ -8833,7 +8883,6 @@
|
|
|
8833
8883
|
} else {
|
|
8834
8884
|
highlight = 0;
|
|
8835
8885
|
}
|
|
8836
|
-
movedSinceOpen = true;
|
|
8837
8886
|
renderPopup();
|
|
8838
8887
|
scrollHighlight();
|
|
8839
8888
|
}
|
|
@@ -8856,8 +8905,10 @@
|
|
|
8856
8905
|
if (document.hidden) return;
|
|
8857
8906
|
const now = wsRunningKeys();
|
|
8858
8907
|
if (lastRunKeys) {
|
|
8908
|
+
const idOf = (k) => k.slice(k.lastIndexOf(":") + 1);
|
|
8909
|
+
const nowIds = new Set([...now].map(idOf));
|
|
8859
8910
|
for (const k of lastRunKeys) {
|
|
8860
|
-
if (!
|
|
8911
|
+
if (!nowIds.has(idOf(k)) && k !== current) unseen.add(k);
|
|
8861
8912
|
}
|
|
8862
8913
|
}
|
|
8863
8914
|
lastRunKeys = now;
|
|
@@ -8874,6 +8925,14 @@
|
|
|
8874
8925
|
closePopup(false);
|
|
8875
8926
|
return;
|
|
8876
8927
|
}
|
|
8928
|
+
const bareK = e.key.toLowerCase();
|
|
8929
|
+
if (popupEl && !e.altKey && !e.ctrlKey && !e.metaKey && !e.isComposing && (bareK === hotkeys.switchPrev || bareK === hotkeys.switchNext)) {
|
|
8930
|
+
e.preventDefault();
|
|
8931
|
+
e.stopImmediatePropagation();
|
|
8932
|
+
if (bareK === hotkeys.switchPrev) moveUp();
|
|
8933
|
+
else moveDown();
|
|
8934
|
+
return;
|
|
8935
|
+
}
|
|
8877
8936
|
if (popupEl && (e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Enter")) {
|
|
8878
8937
|
e.preventDefault();
|
|
8879
8938
|
e.stopImmediatePropagation();
|
|
@@ -8917,22 +8976,29 @@
|
|
|
8917
8976
|
await openPopup("attention");
|
|
8918
8977
|
return;
|
|
8919
8978
|
}
|
|
8920
|
-
if (mru.length < 2) {
|
|
8921
|
-
showToast(t().switcherEmpty);
|
|
8922
|
-
return;
|
|
8923
|
-
}
|
|
8924
8979
|
if (k === hotkeys.switchRecent) {
|
|
8925
|
-
if (!e.repeat && !popupEl
|
|
8980
|
+
if (!e.repeat && !popupEl && !openPopupInflight) {
|
|
8981
|
+
if (!mru.length) {
|
|
8982
|
+
showToast(t().switcherEmpty);
|
|
8983
|
+
return;
|
|
8984
|
+
}
|
|
8985
|
+
await openPopup("mru");
|
|
8986
|
+
}
|
|
8926
8987
|
return;
|
|
8927
8988
|
}
|
|
8928
|
-
if (
|
|
8989
|
+
if (openPopupInflight) {
|
|
8990
|
+
await openPopupInflight;
|
|
8991
|
+
} else if (!popupEl) {
|
|
8992
|
+
if (!mru.length) {
|
|
8993
|
+
showToast(t().switcherEmpty);
|
|
8994
|
+
return;
|
|
8995
|
+
}
|
|
8996
|
+
await openPopup("mru");
|
|
8997
|
+
}
|
|
8929
8998
|
if (k === hotkeys.switchPrev) moveUp();
|
|
8930
8999
|
else moveDown();
|
|
8931
9000
|
})();
|
|
8932
9001
|
}, true);
|
|
8933
|
-
document.addEventListener("keyup", (e) => {
|
|
8934
|
-
if (e.key === "Alt" && popupEl && movedSinceOpen) closePopup(true);
|
|
8935
|
-
}, true);
|
|
8936
9002
|
window.addEventListener("blur", () => closePopup(false));
|
|
8937
9003
|
}
|
|
8938
9004
|
|
|
@@ -8997,7 +9063,7 @@
|
|
|
8997
9063
|
const cur = rows.find((e) => (e.className + "").includes("bg-selected")) || rows[rows.length - 1];
|
|
8998
9064
|
const key = cur ? cur.getAttribute("data-task-item-key") : "";
|
|
8999
9065
|
if (!key) return null;
|
|
9000
|
-
const i = key.
|
|
9066
|
+
const i = key.lastIndexOf(":");
|
|
9001
9067
|
return i > 0 ? { workspacePath: key.slice(0, i), sessionId: key.slice(i + 1) } : null;
|
|
9002
9068
|
}
|
|
9003
9069
|
async function thirdPartySurfaces(bridge, workspacePath) {
|
|
@@ -9562,7 +9628,7 @@
|
|
|
9562
9628
|
var fetching = null;
|
|
9563
9629
|
var pendingKeys = /* @__PURE__ */ new Set();
|
|
9564
9630
|
function sessIdOf(key) {
|
|
9565
|
-
const i = key.
|
|
9631
|
+
const i = key.lastIndexOf(":");
|
|
9566
9632
|
return i > 0 ? key.slice(i + 1) : key;
|
|
9567
9633
|
}
|
|
9568
9634
|
function titleElsOf(item) {
|
|
@@ -9823,7 +9889,7 @@
|
|
|
9823
9889
|
var attempted = /* @__PURE__ */ new Set();
|
|
9824
9890
|
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
9825
9891
|
function sessIdOf2(key) {
|
|
9826
|
-
const i = key.
|
|
9892
|
+
const i = key.lastIndexOf(":");
|
|
9827
9893
|
return i > 0 ? key.slice(i + 1) : "";
|
|
9828
9894
|
}
|
|
9829
9895
|
function currentKey() {
|
|
@@ -11,13 +11,13 @@ import { basename, delimiter, dirname, isAbsolute, join, resolve, sep } from 'no
|
|
|
11
11
|
import { homedir, tmpdir, userInfo } from 'node:os';
|
|
12
12
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
13
13
|
import { readSettings, writeSettingsAtomic, remapSettingsPaths, isProjectOpenInTabs } from './settings.mjs';
|
|
14
|
-
import { taskIndexPath, probeTaskIndexWritable, remapTaskIndexPaths, taskIndexDriverAvailable, listOverriddenTitles, listTaskWorkspaceMap, setTaskTitleIfNotOverridden, setCustomSessionTitle, isTaskTitleOverridden, reconcileCustomTitles, getTaskProviderModel, listVirtualProjects, countTasks } from './taskIndex.mjs';
|
|
14
|
+
import { taskIndexPath, probeTaskIndexWritable, remapTaskIndexPaths, taskIndexDriverAvailable, listOverriddenTitles, listTaskWorkspaceMap, listTaskStatuses, setTaskTitleIfNotOverridden, setCustomSessionTitle, isTaskTitleOverridden, reconcileCustomTitles, getTaskProviderModel, listVirtualProjects, countTasks } from './taskIndex.mjs';
|
|
15
15
|
import { pickFolderSystem } from './pickFolder.mjs';
|
|
16
16
|
import { reorderWorkspaceTasks, reorderGroupMembers } from './taskOrder.mjs';
|
|
17
17
|
import { webdavRequest, webdavPutWithMkcol, webdavHttpText, parseWebdavListing, findCurl } from './webdav.mjs';
|
|
18
18
|
import { resolveZcodeExecutable } from './paths.mjs';
|
|
19
19
|
|
|
20
|
-
const VERSION = '1.5.
|
|
20
|
+
const VERSION = '1.5.1';
|
|
21
21
|
|
|
22
22
|
// 全局提示词固定在用户主目录:官方加载器按 HOME/USERPROFILE 拼 .zcode/AGENTS.md,
|
|
23
23
|
// 不读 ZCODE_DATA_BASE_DIR(数据根迁走时全局指令仍在原位)。
|
|
@@ -707,6 +707,17 @@ export function startHelper({ port, token, dataRoot, state, agentsFile = default
|
|
|
707
707
|
: { ok: true, available: false, projects: [], deletedRoots: [] });
|
|
708
708
|
return;
|
|
709
709
|
}
|
|
710
|
+
if (req.method === 'GET' && url.pathname === '/task-statuses') {
|
|
711
|
+
// 批量会话终态 + 标题:注入侧剔除 syncer 卡滞的误报、alt+r 弹窗显示
|
|
712
|
+
// 折叠项目会话标题都用它。读不到时 available=false,调用方按未确认处理
|
|
713
|
+
const ids = (url.searchParams.get('ids') || '')
|
|
714
|
+
.split(',').map((s) => s.trim()).filter(Boolean).slice(0, 200);
|
|
715
|
+
const statuses = ids.length ? await listTaskStatuses(dataRoot, ids) : {};
|
|
716
|
+
json(res, 200, statuses !== null
|
|
717
|
+
? { ok: true, available: true, statuses }
|
|
718
|
+
: { ok: true, available: false, statuses: {} });
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
710
721
|
if (req.method === 'GET' && url.pathname === '/projects') {
|
|
711
722
|
const settings = readSettings(settingsFile);
|
|
712
723
|
const projects = (settings.recentProjects || []).map((p) => {
|
|
@@ -837,7 +848,9 @@ export function startHelper({ port, token, dataRoot, state, agentsFile = default
|
|
|
837
848
|
json(res, 400, { ok: false, code: 'title-gen-disabled', error: '智能起名未开启' });
|
|
838
849
|
return;
|
|
839
850
|
}
|
|
840
|
-
|
|
851
|
+
// 会话 id 有 sess_ 与 session_ 两种前缀(新版应用为 session_),两种都认,
|
|
852
|
+
// 否则新形式会话的标题永远不会写入数据库
|
|
853
|
+
const sessId = typeof body?.sessId === 'string' && /^(?:sess|session)_[A-Za-z0-9._-]+$/.test(body.sessId) ? body.sessId : '';
|
|
841
854
|
let title;
|
|
842
855
|
try {
|
|
843
856
|
title = await generateTitleWith(cfg, text, dataRoot, sessId);
|
|
@@ -1512,7 +1525,8 @@ export function startHelper({ port, token, dataRoot, state, agentsFile = default
|
|
|
1512
1525
|
renameSync(vTmp, vFile);
|
|
1513
1526
|
}
|
|
1514
1527
|
} catch { /* 无配置或不可写不影响启动 */ }
|
|
1515
|
-
|
|
1528
|
+
// 服务就绪即安排 headroom 代理兜底(内部延迟 5 秒执行,避开启动高峰)
|
|
1529
|
+
server.listen(port, '127.0.0.1', () => { ensureHeadroomAtStartup(); resolveStarted(server); });
|
|
1516
1530
|
};
|
|
1517
1531
|
tryListen();
|
|
1518
1532
|
});
|
|
@@ -1703,12 +1717,13 @@ function mergeTitleGenConfig(saved) {
|
|
|
1703
1717
|
// 登录账号的套餐已在供应商列表里,跟随会话即覆盖原语义
|
|
1704
1718
|
const provider = typeof s.provider === 'string' ? s.provider.trim().slice(0, 200) : d.provider;
|
|
1705
1719
|
// customWritten:起名功能写过的 { sessId → { title, at } },自动校正只认这些
|
|
1706
|
-
// 记录(见 reconcileCustomTitles)。只收合法会话 id
|
|
1720
|
+
// 记录(见 reconcileCustomTitles)。只收合法会话 id(sess_/session_ 两种前缀,
|
|
1721
|
+
// 新版应用为 session_)与短字符串,超 7 天/500 条的丢弃
|
|
1707
1722
|
const written = {};
|
|
1708
1723
|
const cutoff = Date.now() - 7 * 24 * 3600 * 1000;
|
|
1709
1724
|
let kept = 0;
|
|
1710
1725
|
for (const [k, v] of Object.entries((s.customWritten && typeof s.customWritten === 'object' && !Array.isArray(s.customWritten)) ? s.customWritten : {})) {
|
|
1711
|
-
if (kept >= 500 || !/^
|
|
1726
|
+
if (kept >= 500 || !/^(?:sess|session)_[A-Za-z0-9._-]+$/.test(k)) continue;
|
|
1712
1727
|
if (!v || typeof v !== 'object' || typeof v.title !== 'string' || v.title.length > 200) continue;
|
|
1713
1728
|
if (!Number.isFinite(v.at) || v.at < cutoff) continue;
|
|
1714
1729
|
written[k] = { title: v.title, at: v.at };
|
|
@@ -2457,7 +2472,9 @@ export async function classifyIdleBusyWindows(procs, paths, lookupTaskWs) {
|
|
|
2457
2472
|
if (child.ppid !== cli.pid) continue;
|
|
2458
2473
|
if (isInfraCmdline(child.cmdline)) continue;
|
|
2459
2474
|
if (!child.cmdline) { unattributed = true; continue; }
|
|
2460
|
-
|
|
2475
|
+
// 会话 id 有 sess_ 与 session_ 两种前缀(新版应用为 session_);匹配不到
|
|
2476
|
+
// 会走 unattributed 全量判忙——新形式命令挂着时空闲回收会整体停摆
|
|
2477
|
+
const sess = child.cmdline.match(/(?:sess|session)_[A-Za-z0-9-]+/);
|
|
2461
2478
|
if (sess) await markByTask(sess[0]);
|
|
2462
2479
|
else unattributed = true; // 用户进程但无会话线索:归属不到具体项目
|
|
2463
2480
|
}
|
|
@@ -3664,6 +3681,20 @@ async function runHeadroomHook(args, timeoutMs = 30000) {
|
|
|
3664
3681
|
});
|
|
3665
3682
|
}
|
|
3666
3683
|
|
|
3684
|
+
// 启动兜底:整机重启后首次打开应用时恢复的是旧会话,触发 SessionStart(resume),
|
|
3685
|
+
// 旧版 headroom 插件的钩子只匹配 startup,代理不会自动启动(要等用户新建会话或
|
|
3686
|
+
// 手动重启)。helper 就绪后延迟几秒补一次 ensure:动作幂等(已在跑秒回),新版
|
|
3687
|
+
// 插件侧还带跨进程锁;失败一律静默——没装 headroom 插件/本体的环境不能打扰启动。
|
|
3688
|
+
let headroomStartupEnsureFired = false;
|
|
3689
|
+
function ensureHeadroomAtStartup() {
|
|
3690
|
+
if (headroomStartupEnsureFired) return;
|
|
3691
|
+
headroomStartupEnsureFired = true;
|
|
3692
|
+
const timer = setTimeout(() => {
|
|
3693
|
+
runHeadroomHook(['ensure'], 20000).catch(() => { /* 兜底失败不影响启动 */ });
|
|
3694
|
+
}, 5000);
|
|
3695
|
+
timer.unref?.();
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3667
3698
|
// 解析 ensure-proxy(.sh/.mjs) status 输出为结构化字段(字段名以脚本 hr_status 的输出为准;
|
|
3668
3699
|
// headroom 插件 1.3.0+ 多实例模式由 hr-multi 输出同名字段,另附 instance: 行)。
|
|
3669
3700
|
// 导出以便测试脚本直接驱动。
|
|
@@ -241,8 +241,9 @@ export async function listVirtualProjects(dataRoot) {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
-
// 批量查会话终态:task_id →
|
|
245
|
-
// 卡滞的报告(会话已完成但内存态仍报活跃时,任务行 loader
|
|
244
|
+
// 批量查会话终态:task_id → { status, updatedAt, title }。分区头运行判定用它
|
|
245
|
+
// 剔除官方 syncer 卡滞的报告(会话已完成但内存态仍报活跃时,任务行 loader 会
|
|
246
|
+
// 一直卡转);title 供 alt+r 弹窗显示折叠项目(行不在场)会话的标题。
|
|
246
247
|
// 读不到(库缺失/无驱动)返回 null,调用方按未确认处理
|
|
247
248
|
export async function listTaskStatuses(dataRoot, ids) {
|
|
248
249
|
const out = {};
|
|
@@ -252,17 +253,19 @@ export async function listTaskStatuses(dataRoot, ids) {
|
|
|
252
253
|
if (!driver) return null;
|
|
253
254
|
const esc = (v) => "'" + String(v).replace(/'/g, "''") + "'";
|
|
254
255
|
const inList = ids.map((id) => esc(id)).join(', ');
|
|
255
|
-
const sql = `SELECT task_id, task_status, updated_at FROM tasks WHERE task_id IN (${inList})`;
|
|
256
|
+
const sql = `SELECT task_id, task_status, updated_at, title FROM tasks WHERE task_id IN (${inList})`;
|
|
256
257
|
try {
|
|
257
258
|
let rows;
|
|
258
259
|
if (driver.kind === 'node') {
|
|
259
260
|
const db = new driver.DatabaseSync(dbFile, { readOnly: true });
|
|
260
261
|
try { rows = db.prepare(sql).all(); } finally { db.close(); }
|
|
261
262
|
} else {
|
|
262
|
-
|
|
263
|
-
|
|
263
|
+
// 标题可含任意字符(含分隔符),CLI 路径走 JSON 聚合,不做分隔符切分
|
|
264
|
+
const agg = runCli(dbFile,
|
|
265
|
+
`SELECT coalesce(json_group_array(json_object('task_id', task_id, 'task_status', task_status, 'updated_at', updated_at, 'title', title)), '[]') FROM tasks WHERE task_id IN (${inList});`);
|
|
266
|
+
rows = JSON.parse(agg.stdout.trim() || '[]');
|
|
264
267
|
}
|
|
265
|
-
for (const r of rows) out[String(r.task_id)] = { status: String(r.task_status || ''), updatedAt: Number(r.updated_at) || 0 };
|
|
268
|
+
for (const r of rows) out[String(r.task_id)] = { status: String(r.task_status || ''), updatedAt: Number(r.updated_at) || 0, title: String(r.title || '') };
|
|
266
269
|
return out;
|
|
267
270
|
} catch {
|
|
268
271
|
return null;
|
|
@@ -535,7 +538,8 @@ export async function reconcileCustomTitles(dataRoot, writtenMap) {
|
|
|
535
538
|
};
|
|
536
539
|
|
|
537
540
|
for (const [sessId, rec] of Object.entries(writtenMap)) {
|
|
538
|
-
|
|
541
|
+
// 会话 id 有 sess_ 与 session_ 两种前缀(新版应用为 session_),两种都认
|
|
542
|
+
if (!/^(?:sess|session)_[A-Za-z0-9._-]+$/.test(sessId) || !rec || typeof rec.title !== 'string') continue;
|
|
539
543
|
let cli;
|
|
540
544
|
try { cli = await readCli(sessId); } catch { continue; }
|
|
541
545
|
// CLI 已不是我们所写的 custom 标题:别人改过(手动改名/官方),不碰
|