@cteno/sdk-linux-x64 0.0.0-stage → 2.10.13

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.
Files changed (116) hide show
  1. package/LICENSE.daemon +31 -0
  2. package/README.md +4 -2
  3. package/agents/flow-analyzer/AGENT.md +71 -0
  4. package/bin/cteno-host-cli +4 -0
  5. package/bin/cteno-wiki +0 -0
  6. package/bin/rg +0 -0
  7. package/bin/secret-exec +0 -0
  8. package/manifest.json +12 -0
  9. package/package.json +33 -4
  10. package/plugins/agent-secrets/.cteno-plugin/plugin.json +133 -0
  11. package/plugins/agent-secrets/bin/linux-x64/fnox +4 -0
  12. package/plugins/agent-secrets/bin/linux-x64/secret-exec +0 -0
  13. package/plugins/agent-secrets/skills/secret/SKILL.md +201 -0
  14. package/plugins/cteno.browser/.cteno-plugin/plugin.json +65 -0
  15. package/plugins/cteno.browser/skills/browser/SKILL.md +28 -0
  16. package/plugins/cteno.inbox/.cteno-plugin/plugin.json +128 -0
  17. package/plugins/cteno.inbox/bin/all/cteno-connector-credential +31 -0
  18. package/plugins/cteno.inbox/bin/all/cteno-telegram-connector +33 -0
  19. package/plugins/cteno.inbox/bin/linux-x64/cteno-inbox +0 -0
  20. package/plugins/cteno.inbox/skills/cteno-inbox/SKILL.md +374 -0
  21. package/plugins/cteno.inbox/tools/inbox_focus/TOOL.md +44 -0
  22. package/plugins/cteno.memory/.cteno-plugin/plugin.json +176 -0
  23. package/plugins/cteno.memory/agents/llm-wiki/AGENT.md +96 -0
  24. package/plugins/cteno.memory/bin/linux-x64/cteno-wiki +0 -0
  25. package/plugins/cteno.memory/skills/codebase-wiki/SKILL.md +52 -0
  26. package/plugins/cteno.memory/skills/cteno.memory.native/SKILL.md +110 -0
  27. package/plugins/cteno.memory/tools/memory/TOOL.md +492 -0
  28. package/plugins/cteno.memory/ui/index.html +822 -0
  29. package/plugins/cteno.models/.cteno-plugin/plugin.json +191 -0
  30. package/plugins/cteno.websearch/.cteno-plugin/plugin.json +45 -0
  31. package/plugins/jev/.cteno-plugin/plugin.json +305 -0
  32. package/plugins/jev/skills/jev-browser/SKILL.md +66 -0
  33. package/plugins/jev/skills/jev-browser/jev_browser.py +335 -0
  34. package/skills/agent-create/SKILL.md +86 -0
  35. package/skills/cteno-host-cli/SKILL.md +248 -0
  36. package/skills/docx/LICENSE +23 -0
  37. package/skills/docx/NOTICE.md +8 -0
  38. package/skills/docx/SKILL.md +69 -0
  39. package/skills/docx/references/cjk_typography.md +357 -0
  40. package/skills/docx/references/design_principles.md +819 -0
  41. package/skills/docx/references/revisions-and-comments.md +88 -0
  42. package/skills/docx/references/track_changes_guide.md +200 -0
  43. package/skills/docx/references/typography_guide.md +294 -0
  44. package/skills/docx/scripts/cjk_fonts.py +244 -0
  45. package/skills/docx/scripts/docx_comments.py +289 -0
  46. package/skills/docx/scripts/docx_common.py +92 -0
  47. package/skills/docx/scripts/docx_create.py +259 -0
  48. package/skills/docx/scripts/docx_edit.py +250 -0
  49. package/skills/docx/scripts/docx_read.py +149 -0
  50. package/skills/docx/scripts/docx_revisions.py +146 -0
  51. package/skills/docx/scripts/docx_template.py +70 -0
  52. package/skills/docx/scripts/docx_validate.py +156 -0
  53. package/skills/docx/scripts/render.py +250 -0
  54. package/skills/docx/tests/test_docx_skill.py +525 -0
  55. package/skills/home-devices/SKILL.md +121 -0
  56. package/skills/mcp/SKILL.md +72 -0
  57. package/skills/mcp/scripts/mcp_setup.py +439 -0
  58. package/skills/pdf/LICENSE +23 -0
  59. package/skills/pdf/NOTICE.md +8 -0
  60. package/skills/pdf/SKILL.md +63 -0
  61. package/skills/pdf/references/design-tokens.md +381 -0
  62. package/skills/pdf/references/forms.md +99 -0
  63. package/skills/pdf/references/nano-pdf-editing.md +40 -0
  64. package/skills/pdf/references/ocr-extraction.md +165 -0
  65. package/skills/pdf/scripts/_raster.py +76 -0
  66. package/skills/pdf/scripts/extract_marker.py +88 -0
  67. package/skills/pdf/scripts/extract_pymupdf.py +101 -0
  68. package/skills/pdf/scripts/pdf_create.py +191 -0
  69. package/skills/pdf/scripts/pdf_fill_form.py +97 -0
  70. package/skills/pdf/scripts/pdf_form_layout.py +168 -0
  71. package/skills/pdf/scripts/pdf_make_form.py +145 -0
  72. package/skills/pdf/scripts/pdf_merge.py +50 -0
  73. package/skills/pdf/scripts/pdf_meta.py +115 -0
  74. package/skills/pdf/scripts/pdf_page_image.py +99 -0
  75. package/skills/pdf/scripts/pdf_read.py +153 -0
  76. package/skills/pdf/scripts/pdf_secure.py +71 -0
  77. package/skills/pdf/scripts/pdf_split.py +84 -0
  78. package/skills/pdf/scripts/pdf_stamp.py +143 -0
  79. package/skills/pdf/scripts/pdf_watermark.py +51 -0
  80. package/skills/pdf/scripts/render.py +250 -0
  81. package/skills/pdf/tests/test_pdf_skill.py +415 -0
  82. package/skills/pptx/LICENSE +23 -0
  83. package/skills/pptx/NOTICE.md +8 -0
  84. package/skills/pptx/SKILL.md +62 -0
  85. package/skills/pptx/references/design-system.md +392 -0
  86. package/skills/pptx/references/pptxgenjs.md +420 -0
  87. package/skills/pptx/references/slide-types.md +413 -0
  88. package/skills/pptx/scripts/cjk_fonts.py +244 -0
  89. package/skills/pptx/scripts/pptx_create.py +240 -0
  90. package/skills/pptx/scripts/pptx_edit.py +436 -0
  91. package/skills/pptx/scripts/pptx_from_template.py +88 -0
  92. package/skills/pptx/scripts/pptx_read.py +131 -0
  93. package/skills/pptx/scripts/pptx_render.py +96 -0
  94. package/skills/pptx/scripts/render.py +250 -0
  95. package/skills/pptx/tests/test_powerpoint_skill.py +477 -0
  96. package/skills/skill-create/SKILL.md +114 -0
  97. package/skills/video-generation/SKILL.md +27 -0
  98. package/skills/xlsx/LICENSE +23 -0
  99. package/skills/xlsx/NOTICE.md +8 -0
  100. package/skills/xlsx/SKILL.md +63 -0
  101. package/skills/xlsx/references/format.md +71 -0
  102. package/skills/xlsx/references/restructuring.md +71 -0
  103. package/skills/xlsx/scripts/csv_to_xlsx.py +110 -0
  104. package/skills/xlsx/scripts/formula_check.py +428 -0
  105. package/skills/xlsx/scripts/render.py +250 -0
  106. package/skills/xlsx/scripts/xlsx_create.py +265 -0
  107. package/skills/xlsx/scripts/xlsx_edit.py +269 -0
  108. package/skills/xlsx/scripts/xlsx_read.py +166 -0
  109. package/skills/xlsx/scripts/xlsx_recalc.py +119 -0
  110. package/skills/xlsx/scripts/xlsx_restructure.py +343 -0
  111. package/skills/xlsx/scripts/xlsx_to_csv.py +75 -0
  112. package/skills/xlsx/tests/test_xlsx_skill.py +544 -0
  113. package/tools/create_goal/TOOL.md +36 -0
  114. package/tools/image_generation/TOOL.md +81 -0
  115. package/tools/skill/TOOL.md +117 -0
  116. package/tools/update_profile_notes/TOOL.md +39 -0
package/LICENSE.daemon ADDED
@@ -0,0 +1,31 @@
1
+ Cteno Daemon Binary License
2
+
3
+ Copyright (c) Cteno contributors. All rights reserved.
4
+
5
+ This license covers the compiled Cteno daemon and helper programs shipped in
6
+ the platform packages of the Cteno SDK (npm `@cteno/sdk-<platform>`, the
7
+ `cteno_sdk/_bin` directory of the `cteno-sdk` platform wheels): `cteno-host-cli`,
8
+ `cteno-wiki`, `secret-exec`, and the bundled resource files beside them.
9
+ The SDK client libraries themselves (`@cteno/sdk`, `cteno_sdk/*.py`) are
10
+ licensed under the MIT License, see LICENSE.
11
+
12
+ 1. Grant. You may install and run these binaries, free of charge, on any
13
+ number of machines, for any purpose including commercial use, as part of
14
+ software that uses the Cteno SDK. You may redistribute them unmodified,
15
+ inside the official package or as a dependency of your own software.
16
+
17
+ 2. Restrictions. You may not modify, decompile, disassemble or reverse
18
+ engineer the binaries except where applicable law expressly permits it
19
+ despite this restriction, and you may not remove or alter this notice.
20
+
21
+ 3. Third-party components. The binaries include open-source components under
22
+ their own licenses (for example ripgrep, MIT/Unlicense). Those licenses
23
+ apply to those components.
24
+
25
+ 4. No warranty. THE BINARIES ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
26
+ KIND, EXPRESS OR IMPLIED, INCLUDING MERCHANTABILITY, FITNESS FOR A
27
+ PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
28
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM THEIR USE.
29
+
30
+ 5. Termination. This license ends automatically if you breach it; you must
31
+ then stop using and distributing the binaries.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
- # Temporary Holding Version
1
+ # @cteno/sdk-linux-x64
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The Cteno daemon binary for `@cteno/sdk` on linux-x64. Install `@cteno/sdk`; npm picks this package automatically. Do not depend on it directly.
4
+
5
+ The binaries are not MIT; see LICENSE.daemon in this package. The SDK client itself is MIT.
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: "Flow Analyzer"
3
+ description: "Analyzes orchestration scripts and outputs flow visualization JSON"
4
+ version: "1.0.0"
5
+ type: "autonomous"
6
+ expose_as_tool: false
7
+ session:
8
+ enabled: false
9
+ model: "deepseek-chat"
10
+ temperature: 0.1
11
+ max_tokens: 4096
12
+ allowed_tools:
13
+ - read
14
+ - glob
15
+ - grep
16
+ ---
17
+
18
+ # Flow Analyzer
19
+
20
+ You analyze orchestration scripts (bash, python, etc.) and output a structured
21
+ flow JSON that describes the execution graph for visualization.
22
+
23
+ ## Task
24
+
25
+ Given a script file path, you must:
26
+
27
+ 1. **Read the script** using the `read` tool
28
+ 2. **Find all `cteno-host-cli dispatch` calls** and extract:
29
+ - `--label` (node ID)
30
+ - `-m` / `--message` (task description, used as node label)
31
+ - `-t` / `--type` (agent type)
32
+ 3. **Analyze control flow**:
33
+ - Sequential dispatches = normal edges between consecutive nodes
34
+ - `if/else` branches = conditional edges with condition labels
35
+ - `while/for` loops = retry edges (back edge from loop end to loop start)
36
+ - `&&` chains = normal sequential edges
37
+ 4. **Output valid JSON** in exactly this format:
38
+
39
+ ```json
40
+ {
41
+ "title": "Human-readable flow title",
42
+ "nodes": [
43
+ {
44
+ "id": "step-1",
45
+ "label": "Implement feature",
46
+ "agentType": "worker"
47
+ },
48
+ {
49
+ "id": "test-step",
50
+ "label": "Run tests",
51
+ "agentType": "worker",
52
+ "maxIterations": 3
53
+ }
54
+ ],
55
+ "edges": [
56
+ { "from": "step-1", "to": "test-step", "edgeType": "normal" },
57
+ { "from": "test-step", "to": "step-1", "edgeType": "retry" }
58
+ ]
59
+ }
60
+ ```
61
+
62
+ ## Rules
63
+
64
+ - Every `cteno-host-cli dispatch --label X` in the script MUST have a corresponding node with `id: "X"`
65
+ - Node `id` must exactly match the `--label` value
66
+ - Node `label` should be a short human-readable description (from `-m` or inferred)
67
+ - For loops, set `maxIterations` from the loop counter variable if possible
68
+ - For conditional branches, set `edgeType: "conditional"` and `condition: "pass"` or `condition: "fail"`
69
+ - Retry edges go from a later node back to an earlier node
70
+ - Output ONLY the JSON object, no markdown fences, no explanation
71
+ - If the script has no `cteno-host-cli dispatch` calls, output `{"title": "No dispatches found", "nodes": [], "edges": []}`
@@ -0,0 +1,4 @@
1
+ [diffend] Oversized file quarantined before diffing.
2
+ name: package/bin/cteno-host-cli
3
+ size: 35741944 bytes
4
+ sha256: 2d28e9a748ec6de28ef50a8c2de4494e29ef33cab365eefab9b4995665cf7cdd
package/bin/cteno-wiki ADDED
Binary file
package/bin/rg ADDED
Binary file
Binary file
package/manifest.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "sku": "sdk-core",
4
+ "version": "2.10.12",
5
+ "target": "linux-x64",
6
+ "binaries": [
7
+ "cteno-host-cli",
8
+ "cteno-wiki",
9
+ "secret-exec",
10
+ "rg"
11
+ ]
12
+ }
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "@cteno/sdk-linux-x64",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.10.13",
4
+ "description": "Bundled Cteno daemon (cteno-host-cli 2.10.12) for @cteno/sdk on linux-x64",
5
+ "license": "SEE LICENSE IN LICENSE.daemon",
6
+ "homepage": "https://github.com/zalan159/cteno-sdk#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/zalan159/cteno-sdk.git"
10
+ },
11
+ "os": [
12
+ "linux"
13
+ ],
14
+ "cpu": [
15
+ "x64"
16
+ ],
17
+ "files": [
18
+ "bin",
19
+ "tools",
20
+ "skills",
21
+ "agents",
22
+ "plugins",
23
+ "manifest.json",
24
+ "LICENSE.daemon",
25
+ "README.md"
26
+ ],
27
+ "preferUnplugged": true,
28
+ "cteno": {
29
+ "daemonVersion": "2.10.12"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public",
33
+ "provenance": true
34
+ }
35
+ }
@@ -0,0 +1,133 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "id": "agent-secrets",
4
+ "version": "0.1.6",
5
+ "title": "Agent Secrets",
6
+ "icon": "lock-closed-outline",
7
+ "color": "#3DDC97",
8
+ "developer": "Cteno",
9
+ "category": "security",
10
+ "origin": "builtin",
11
+ "featured": true,
12
+ "defaultEnabled": true,
13
+ "description": "Let the agent use API keys and passwords to get work done without the plaintext ever entering the conversation. Cteno agents reference a secret inline as secret://vault/<name> and the runtime injects it downstream; guest agents (Claude Code / Codex) call the bundled secret-exec CLI. age-encrypted local vault (fnox); no daemon, no cloud. Published standalone at github.com/zalan159/agent-secrets.",
14
+ "surfaces": {
15
+ "auth": {
16
+ "mode": "terminal",
17
+ "command": "secret-exec unlock"
18
+ },
19
+ "rpc": {
20
+ "handler": {
21
+ "type": "command",
22
+ "command": "secret-exec",
23
+ "args": ["rpc"],
24
+ "timeout_secs": 60
25
+ },
26
+ "methods": [
27
+ "status",
28
+ "list",
29
+ "lock",
30
+ "delete",
31
+ "describe",
32
+ "platform.secureInput.begin",
33
+ "platform.secureInput.submit"
34
+ ]
35
+ },
36
+ "secureInput": {
37
+ "actions": {
38
+ "init": {
39
+ "args": ["init", "--stdin"],
40
+ "successMessage": "保险箱已创建并解锁"
41
+ },
42
+ "unlock": {
43
+ "args": ["unlock"],
44
+ "successMessage": "保险箱已解锁"
45
+ },
46
+ "set": {
47
+ "args": ["set", "{id}"],
48
+ "successMessage": "已存入"
49
+ }
50
+ }
51
+ },
52
+ "secretProvider": {
53
+ "scheme": "vault",
54
+ "resolveArgv": [
55
+ "secret-exec",
56
+ "get",
57
+ "{id}"
58
+ ],
59
+ "credentialEnv": {
60
+ "FNOX_AGE_KEY_FILE": "{root}/.identity.session"
61
+ }
62
+ },
63
+ "externalCli": {
64
+ "name": "Agent Secrets CLI",
65
+ "description": "No-daemon secret wrapper: `secret-exec run \"<cmd>\"` resolves secret://vault/<id> refs, injects the value downstream, and scrubs it from the output. `unlock`/`lock`/`status`/`list`/`import` manage the vault. Usable by any guest agent (Claude Code / Codex) via the shell.",
66
+ "binary": "secret-exec",
67
+ "authCommand": "secret-exec unlock",
68
+ "statusCommand": "secret-exec status"
69
+ },
70
+ "prompt": {
71
+ "fragments": [
72
+ "skills/secret/SKILL.md"
73
+ ]
74
+ },
75
+ "suggestedPrompts": [
76
+ {
77
+ "id": "list",
78
+ "title": "看看我存了哪些密码和密钥",
79
+ "prompt": "看看我的保险箱里存了哪些密码和密钥:只列名字和用途,不要显示任何值。"
80
+ }
81
+ ],
82
+ "recommendations": [
83
+ {
84
+ "id": "agent-secrets-init",
85
+ "title": "初始化保险箱(设总密码)",
86
+ "description": "第一次用:在 Cteno 安全面板设置总密码,后台完成建仓,不进入聊天。",
87
+ "icon": "shield-checkmark-outline",
88
+ "color": "#3DDC97",
89
+ "prompt": "帮我初始化本地密钥保险箱。先解释总密码保护密钥备份和换机恢复、丢失无法找回。本机优先在设置的密码与密钥页点击创建保险箱,用受信任安全输入面板设置并确认总密码,后台完成初始化,不弹终端、不在聊天收密码。若已有等待中的密钥请求,建仓后续办同一请求。建好后提醒只备份加密的 fnox.toml 与 identity.age,不备份会话私钥。远程首次建仓暂不可用。"
90
+ },
91
+ {
92
+ "id": "agent-secrets-import",
93
+ "title": "导入已有密钥",
94
+ "description": "把 .env / 现有配置里的 key 一次性加密导入保险箱(加密即入库,明文不进对话)。",
95
+ "icon": "download-outline",
96
+ "color": "#3DDC97",
97
+ "prompt": "帮我把已有的密钥导入本地保险箱。先问我源文件路径(如 ~/.env 或某个 json),再用 `secret-exec import <文件>` 导入——加密即入库,绝不 echo/cat/打印任何值。导入后用 `secret-exec list` 只列名字确认,并提醒我删掉源明文文件。"
98
+ },
99
+ {
100
+ "id": "agent-secrets-list",
101
+ "title": "看有哪些密钥",
102
+ "description": "列出保险箱里已存的密钥名字(只看名字,绝不显示值)。",
103
+ "icon": "list-outline",
104
+ "color": "#3DDC97",
105
+ "prompt": "列出我本地保险箱里有哪些密钥——用 `secret-exec list`,只显示名字,不要读取或显示任何密钥的值。"
106
+ },
107
+ {
108
+ "id": "agent-secrets-backup",
109
+ "title": "加密备份保险箱",
110
+ "description": "把密文 fnox.toml + passphrase 包裹的 identity.age 备份到 iCloud/网盘(零知识,别人拿到也解不开)。",
111
+ "icon": "cloud-upload-outline",
112
+ "color": "#3DDC97",
113
+ "prompt": "帮我加密备份本地保险箱。先问我备份目标目录(如 iCloud Drive 某文件夹),然后只复制密文 `fnox.toml` 和 passphrase 包裹的 `identity.age` 过去——绝不复制任何解密后的明文或裸 age 身份(如 identity.txt / .identity.session)。完成后说明备份了哪些文件、以及新设备怎么恢复(放回原位 + `secret-exec unlock` 输口令)。"
114
+ },
115
+ {
116
+ "id": "agent-secrets-demo",
117
+ "title": "用密钥调个 API 试试",
118
+ "description": "演示核心能力:用保险箱里的 token 调 API,token 只到下游、全程不进对话。",
119
+ "icon": "flash-outline",
120
+ "color": "#3DDC97",
121
+ "prompt": "演示保险箱怎么用:先 `secret-exec list` 看我有哪些 token,让我选一个,再用它调对应的 API——命令里用 `secret://vault/<名字>` 引用,别写明文。给我看结果,并确认 token 全程没出现在对话里(会显示成 ***)。"
122
+ }
123
+ ]
124
+ },
125
+ "provides": {
126
+ "bin": ["fnox", "secret-exec"]
127
+ },
128
+ "permissions": {
129
+ "externalProcess": ["fnox", "secret-exec"],
130
+ "network": false,
131
+ "filesystem": []
132
+ }
133
+ }
@@ -0,0 +1,4 @@
1
+ [diffend] Oversized file quarantined before diffing.
2
+ name: package/plugins/agent-secrets/bin/linux-x64/fnox
3
+ size: 74515392 bytes
4
+ sha256: eb7e2d2096b4c64d11c14a48e1be57e34ff99700cfc8efe734d58bb18540d47c
@@ -0,0 +1,201 @@
1
+ ---
2
+ name: agent-secrets
3
+ description: 帮用户管理和使用本地加密保险箱里的密钥(API key / token / 密码 / SSH key)。用户让你导入、使用或备份密钥,或命令需要凭据时用本 skill。密钥明文永不进对话;本机建仓和解锁优先用 Cteno 受信任安全输入面板。
4
+ ---
5
+
6
+ # Agent Secrets —— 帮用户用密钥,明文不进对话
7
+
8
+ 用户有一个本地**加密保险箱**(`~/.config/agent-secrets/`)。你能帮他 **导入、使用、备份** 密钥,但**永远看不到明文**,明文也**永不进对话**。把用户当小白,一步步引导。
9
+
10
+ ## 铁律(先记牢)
11
+
12
+ - **总密码只在受信任安全输入面板输。** 本机建仓/解锁优先走 Cteno 安全面板;不在聊天收取、不进普通 RPC/argv、不由 Agent 持有。外部 guest CLI 的手工 TTY 仅作兜底;`init --stdin` 专供后台安全通道,Agent 不应收取总密码后代填。
13
+ - **绝不** `echo`/`cat`/`printenv` 任何密钥的值,**绝不**为绕过打码去 base64/hash/反转。用密钥只为完成用户的任务。
14
+ - 不确定密钥名就问用户,别猜。
15
+
16
+ ## 用户动线(按这个判断该做什么)
17
+
18
+ ### A-1. 先问"箱里有没有",再决定要不要问人 ⭐
19
+
20
+ **任何时候你觉得需要一个凭据,第一步不是弹窗,是 `op:"list"`。**
21
+
22
+ 用户最烦的一件事就是被反复索要**上周已经给过**的东西。你看不到保险箱内容,
23
+ 所以除非先查,否则你**没有依据**判断该不该问。
24
+
25
+ ```
26
+ request_secret(op: "list", query: "database")
27
+ → { compose: "secret://vault/<id>", state: "ok", total: 113,
28
+ ids: ["cteno/production/DATABASE_URL"] }
29
+ ```
30
+
31
+ - **`query` 是子串搜索,务必用**。保险箱里的命名并不统一(`cteno/ALIYUN_AK_SECRET`
32
+ 旁边就躺着扁平的 `ALIYUN_MAIN_AK_ID`),**精确猜名基本必败**,只能搜。
33
+ 想不出词就先不带 `query` 看全量,再缩。
34
+ - 拿 `compose` 自己拼引用:`secret://vault/` + id。**列表里不重复前缀**(省上下文)。
35
+ - **`state`**:`ok` 可直接解析;`locked` 表示**名字在、需要解锁**(走 **D**,
36
+ 别误判成"不存在"转头去问用户);`uninitialized` 是还没建仓(走 **C**)。
37
+ - 命中了就**直接用**,一个弹窗都不要出。没命中才走 **A0**。
38
+ - `list` **只列 vault**,不列 ephemeral——一次性值用完即焚,复用是错的。
39
+
40
+ ### A0. 你需要一个**保险箱里没有**的值 → 用 `request_secret` 工具
41
+
42
+ (前提:已经按 **A-1** 查过、确认没有。)
43
+
44
+ sudo 密码、短信验证码、一次性解压密码、某个从没存过的 API key——**别在聊天里要**(明文会进 transcript、账本和模型上下文,正是保险箱要挡的东西),也**别弹终端**(那只在用户正好坐在这台电脑前才有用)。
45
+
46
+ 直接调 `request_secret`:宿主会在用户**手上那台设备**(桌面 / 手机 / 网页都行)弹一个可信输入框,用户输完,你拿到的**只有一个 `secret://` 引用**,永远看不到值。
47
+
48
+ ```
49
+ request_secret(label: "服务器 sudo 密码", purpose: "重启 nginx", persist: false)
50
+ → secret://ephemeral/<id>
51
+ ```
52
+
53
+ 两种去向,按"这个值以后还要不要"选:
54
+
55
+ - **`persist: false`(默认,ephemeral)**——sudo 密码、验证码这类**用完就扔**的。值只在 daemon 内存里,**解析一次即销毁**,daemon 重启就没了。也**避开死锁**:写保险箱要求保险箱先解锁,而用户往往正是因为锁着才要输密码。
56
+ - **`persist: true` + `name: "<名字>"`**——要长期留着的 API key 或账号密码。写进 Cteno 保险箱,返回 `secret://vault/<名字>`,**跨会话、跨重启都能用**。
57
+
58
+ **命名规则:** 支持 `cteno/connectors/xxx/credential-v1` 这样的**分层名**(最长 128 字符),
59
+ 与保险箱里既有的 canonical ID 一致——`op:"list"` 看到什么名字,就能用什么名字写回去。
60
+ 禁止 `..`、前导/尾随 `/`、连续 `//`、前导 `-` 和空白字符。
61
+ 既有分层 ID **不要改名或批量迁移**。
62
+
63
+ **持久化前会自动兜底:** `persist: true` 时宿主会再查一次保险箱,
64
+ **按叶子段**比对(`cteno/SMTP_PASS` 与 `SMTP_PASS` 视为同一个)。
65
+ 命中就直接返回既有引用、**零弹窗**,不会多出一条重复条目。
66
+
67
+ **可迁移性硬规则:** 长期凭据统一进入 Cteno Secret Vault。不要因为当前机器是 macOS 就退化保存到 Login Keychain,也不要把 Windows Credential Manager 或 Linux Secret Service 当作密码真值;这些系统存储不随 Cteno Vault 备份迁移。只有用户明确要求使用系统密码库时才可例外,并应说明迁移代价。
68
+
69
+ #### 项目级授权(`.cteno/secrets.json`)
70
+
71
+ 项目可以声明"本项目只许用这几条"。有声明时:
72
+
73
+ - `op:"list"` **只列声明内**的条目;
74
+ - 声明外的引用**解析会被拒**(`is not allowed in this project`),
75
+ 不是"不存在",也不是"锁着"。
76
+
77
+ 看到这个报错,**不要自己去改 `.cteno/secrets.json`** ——
78
+ 那是用户的授权决定,agent 自行放宽等于这道闸门不存在。
79
+ 告诉用户缺哪一条、为什么需要,由用户添加。
80
+
81
+ 没有这个文件 = 不设限(既有项目行为不变);
82
+ `{"allow": []}` 是**真实决定**,表示本项目一条都不给。
83
+
84
+ 想按历史用量生成初稿:`node scripts/secrets-allowlist-bootstrap.mjs --project <dir>`
85
+ (默认只打印不写;`--write` 也拒绝覆盖已有文件)。
86
+
87
+ 拿到引用后照 **A** 用:把引用**原样内联**写进命令即可。
88
+
89
+ - 没人应答会超时失败(默认 300 秒)。**别循环重试,更别改口在聊天里要**——告诉用户你需要这个值,然后停下。
90
+ - **无人值守时立刻失败、不空等**:定时任务 / 后台 session 根本没有能弹窗的界面,
91
+ 这时会**秒返回** `no_interactive_channel`,不会干等 5 分钟。
92
+ 照常记下"缺哪个凭据",继续做别的,别重试。
93
+ - 用户点了拒绝,同样停下,别换个说法再问一遍。
94
+
95
+ ### A. 用户要「用某个密钥」(调 API、git push、连数据库…)
96
+
97
+ 命令里把密钥**内联**引用,运行时自动注入、输出里打码成 `***`:
98
+
99
+ ```
100
+ curl -s -H "Authorization: Bearer secret://vault/github-token" https://api.github.com/user
101
+ ```
102
+
103
+ **所有对外工具都支持**,引用可以放在参数树的**任意深度**:
104
+
105
+ | 工具 | 写法 |
106
+ |---|---|
107
+ | `shell` | 直接写进命令字符串 |
108
+ | `browser` | `{"action":{"kind":"fill-input","text":"secret://vault/X"},"backendNodeId":N}` |
109
+ | `computer_use` | `{"action":"type","text":"secret://vault/X"}` ——**桌面 App 登录框同样可用** |
110
+ | 插件工具 / MCP | 任意字符串参数 |
111
+
112
+ 账本、transcript 和下一轮模型上下文里留下的**始终是引用**,
113
+ 只有真正执行的那一份带明文,返回值里的明文会被打回 `***`。
114
+
115
+ **刻意不解析的**:`dispatch_task` / `memory` / `ask_owner` 这类"进上下文、进持久化"的工具
116
+ 保持引用原样——子 session 会在自己的执行边界解析,
117
+ 提前解开等于把明文写进另一段上下文或一个持久化页面。
118
+
119
+ - 报 **`identity_not_found` / 保险箱锁着** → 走 **D. 解锁**。
120
+ - 报 **保险箱不存在 / `fnox.toml` No such file** → 走 **C. 建仓**(还没建过)。
121
+ - 报 **`is not allowed in this project`** → 项目 allowlist 拦下的,见上文「项目级授权」。
122
+
123
+ ### B. 用户要「导入已有密钥」(.env / SSH key / 某个 json…)
124
+
125
+ 先确认保险箱建过没(跑一下 `secret-exec list`):
126
+ - 没建 → 先走 **C. 建仓**,再导入。
127
+ - 建好了 → 打包成文件喂给 `secret-exec import`(**加密即入库,免解锁,只用公钥**):
128
+ - `.env` 直接:`secret-exec import ~/.env`
129
+ - 多个 SSH key:用 Python 把它们读成一个 JSON(**别打印内容**),再 `secret-exec import <json> --format json`。
130
+ - 导入完 `secret-exec list` 只列名字确认,**提醒用户删掉源明文文件**,然后走 **E. 备份提醒**。
131
+
132
+ ### C. 建仓(保险箱还没有 —— 引导小白设总密码)
133
+
134
+ **不要自己手搭 fnox 配置,也不要默认弹终端。** 本机密钥请求卡检测未初始化后提供「创建保险箱」,或在设置 ▸ 密码与密钥创建。受信任安全面板一次显示总密码与确认两栏;临时加密握手不依赖保险箱,后台以 stdin 完成建仓并自动解锁,重验后续办同一请求。取消面板保留原请求,取消请求或重启后不得复活旧等待者。总密码不得进入聊天、Agent、日志或普通参数。
135
+
136
+ 手机/远程首次建仓和解锁尚未具备独立设备身份校验,当前拒绝收取密码,不得声称已支持。外部 guest CLI 没有 Cteno 界面时仍可让用户手工运行 TTY `secret-exec init`,不要代填总密码。建仓后走 **E. 备份提醒**。
137
+
138
+ ### D. 解锁(**一次就够,别每次用都解锁**)
139
+
140
+ 保险箱像 keychain:**解锁一次,整个 Cteno agent(所有会话)就都能用,直到用户 `lock` 或重启**。
141
+
142
+ - **刚 `init` 建完的仓已经是解锁状态**,直接用,别再让用户解锁。
143
+ - 只有当**真的锁着**时才解锁——判据:用/解析密钥报 `identity_not_found` / `locked`(别没报错就主动解锁)。
144
+
145
+ **首选:本机请求卡的「解锁保险箱」或设置 ▸ 密码与密钥的「解锁」**,总密码只在受信任安全面板填写。
146
+
147
+ > 临时公钥由目标 daemon 生成,与保险箱无关;输入只以密文提交,后台解密后直接经 stdin 交给插件。当前仅认证的目标主机本地 owner IPC 可建仓/解锁,远程客户端即使已登录或配对也不能绕过。
148
+
149
+ 同一个页面还能看已存密钥的**名字**(只有名字,永远不显示值)、上锁、以及存新密钥(值同样走那个安全输入框,不进对话)。
150
+
151
+ **桌面兜底**:用户就在这台电脑前、又懒得开界面时,才弹终端:
152
+ ```
153
+ osascript -e 'tell application "Terminal" to activate' -e 'tell application "Terminal" to do script "secret-exec unlock"'
154
+ ```
155
+ 让用户在终端输总密码,看到 `unlocked: age1…` 回来说“好了”,你重试。
156
+
157
+ - 无论走哪条路,**解锁一次就够**——这一整段对话里再用别的密钥都不用再解锁,别反复弹终端。
158
+ - **绝不**在聊天里收密码:不要让用户把总密码打进对话,也不要自己去读它。密码只属于那个安全输入框或那个终端。
159
+ - 解锁口令**只有总密码**,不存在其它“生成的口令”:`AGE-SECRET-KEY-…` 私钥从不显示、从不用于解锁;终端打印的 `age1…` 是公钥,只能加密解不开。
160
+ - 新版 `secret-exec unlock` 在真实终端会用隐藏输入提示(rpassword);管道方式(`printf '%s' "$PASS" | secret-exec unlock`)仍兼容。
161
+
162
+ ### D2. 存一条密钥(值不进 argv、不进对话)
163
+
164
+ - 让用户自己在保险箱页面点「添加密钥」:先填**名字**,值在安全输入框里输——多端可用,值不经过你。
165
+ - `secret-exec set <id>` 当前通过 Fnox env 写入,因此这里的 `<id>` 必须使用 `^[A-Z][A-Z0-9_]*$`:例如邮箱密码用 `EMMA_CTENO_MAIL_PASSWORD`,Instagram 密码用 `INSTAGRAM_EMMA_CTENO_PASSWORD`;不要用 `emma-password`、`emma.password` 或含空格/中文的名字。**这不是全 Vault 命名限制**:既有 `cteno/connectors/...` 分层 ID 保持原样。
166
+ - 你自己要写入时用 `secret-exec set <id>`,值**只从 stdin 读**:
167
+ ```
168
+ printf '%s' "$VALUE" | secret-exec set OPENAI_API_KEY
169
+ ```
170
+ **绝不要**把值写进命令行参数(`ps` 同用户可见)。加密只需要公钥,所以**锁着也能存**。若 Fnox 因既有分层 ID 对整份 `fnox.toml` 报大量 environment-variable validation 错误,停止并改用 Cteno Vault UI/宿主持久化入口;不得为让命令通过而重写既有 ID。
171
+ - 持久密码和 token 的真值统一在 Cteno Vault。不得自行改存 macOS Keychain、Windows Credential Manager 或 Linux Secret Service;若历史凭据在那里,优先通过 Cteno 安全 Vault UI/宿主入口迁移,只有确认目标名称已存在后才能删除旧项,全程不得打印明文。
172
+ - 整文件批量导入仍用 `secret-exec import <file>`。
173
+
174
+
175
+ ### E. 备份提醒(建仓/导入后一定要说)
176
+
177
+ 小白话提醒用户:
178
+ > “建议把保险箱备份一下:`~/.config/agent-secrets/` 里的 **`fnox.toml`** 和 **`identity.age`** 两个文件复制到 iCloud / 网盘 / U 盘。它们是加密的,别人拿到也解不开(要你的总密码)。换新电脑时把这两个文件放回原位,再 `secret-exec unlock` 输总密码就恢复了。**只备份这两个,别的都不用。**”
179
+
180
+ 想让我直接帮他备份,就走「加密备份保险箱」的做法:只 `cp` 这两个密文文件到他指定目录,**绝不**复制任何解密后的明文或裸身份(`identity.txt` / `.identity.session`)。
181
+
182
+ ### F. 迁移到新机器(换机 / 换账号 / 换用户)
183
+
184
+ - 只复制两个密文文件:`fnox.toml` + `identity.age`,放到新机器 `~/.config/agent-secrets/`(目录 700、文件 600)。
185
+ - **绝不复制**:`age.key`(明文恢复私钥,留在原机)、`.identity.session`(会话解锁态)、任何明文。
186
+ - 新机器装好 `secret-exec`(和 fnox)后,`secret-exec unlock` 输**总密码**即恢复;多机共用同一 vault 没问题(零知识,靠总密码)。
187
+ - ⚠️ **血泪教训 1:不要用 `age` 命令行做包裹/重设**(`echo pass | age -p …` 无效)。`age -p` 忽略管道、直接在终端弹 “Enter passphrase (leave empty to autogenerate a secure one):”,真正生效的是提示里输的内容(回车=自动生成的随机口令),用户会以为设的是自己的密码。包裹/重设一律走 `secret-exec`(与 `init`/`unlock` 同一套 Rust age scrypt 代码)。
188
+ - ⚠️ **血泪教训 2:不要把迁移来的 macOS 登录钥匙串加进新机器默认 keychain 搜索列表**(`security list-keychains -d user -s` 加入后,其它二进制做普通 keychain 查询会弹它的密码框)。迁移的 `.keychain-db` 只保留文件,需要时在「钥匙串访问」里手动打开。
189
+
190
+ ### G. 忘记总密码 → 改密码(rekey,私钥不变)
191
+
192
+ 前提:任一机器上有 `age.key`(或已解锁的 `.identity.session`)——这是改密码所需的“恢复身份”。
193
+
194
+ 1. 独立终端跑 `secret-exec rekey`(macOS 用 osascript 弹终端),隐藏输入两遍**新**总密码。
195
+ 2. 私钥与 recipient 不变,`fnox.toml` 不用动;旧 `identity.age` 自动备份为 `identity.age.before-rekey-<ts>`。
196
+ 3. 把新的 `identity.age` 同步到其它机器(`fnox.toml` 不用动)。
197
+ 4. 之后所有机器 `unlock` 都只认新总密码。
198
+
199
+ ---
200
+
201
+ > 在 guest host(Claude Code / Codex)下没有内联解析:把命令包进 `secret-exec run '<cmd>'`。建仓/解锁/导入/备份的引导流程一样。
@@ -0,0 +1,65 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "id": "cteno.browser",
4
+ "version": "0.1.1",
5
+ "title": "Cteno Browser",
6
+ "icon": "globe-outline",
7
+ "color": "#2F80ED",
8
+ "developer": "Cteno",
9
+ "category": "浏览器",
10
+ "featured": true,
11
+ "origin": "builtin",
12
+ "description": "Cteno 的浏览器能力包:daemon-owned CEF 引擎 + 结构化 browser 命令面(open / semantic-snapshot / semantic-step / js / screenshot / tabs)。引擎无 GUI 也可独立使用(Linux headless with browser);有 GUI 时 pane 作为该引擎的 viewer(OSR presentation 通道),并支持远程 viewer 流送。禁用本插件后 browser-* RPC 与 runtime browser 工具报 capability 不可用。",
13
+ "defaultEnabled": true,
14
+ "essential": true,
15
+ "surfaces": {
16
+ "prompt": {
17
+ "fragments": [
18
+ "skills/browser/SKILL.md"
19
+ ]
20
+ },
21
+ "daemonRpc": {
22
+ "capability": "browser",
23
+ "unavailableCode": "browser_capability_unavailable",
24
+ "methods": [
25
+ "browser-pane-open",
26
+ "browser-pane-new-tab",
27
+ "browser-pane-close",
28
+ "browser-pane-switch-tab",
29
+ "browser-pane-close-tab",
30
+ "browser-pane-destroy-session",
31
+ "browser-viewer-endpoint",
32
+ "browser-tab-command",
33
+ "browser-helper-command"
34
+ ]
35
+ },
36
+ "presentation": {
37
+ "contractVersion": 1,
38
+ "channels": {
39
+ "darwin": "iosurface-mach",
40
+ "windows": "dxgi-shared-texture",
41
+ "linux": null
42
+ },
43
+ "fallback": "cdp-screencast"
44
+ },
45
+ "engine": {
46
+ "provider": "cef-headless",
47
+ "distribution": "oss-index",
48
+ "installTime": true,
49
+ "note": "CEF 大二进制不进插件 zip,也不进 Git:安装/升级时经既有 OSS index + parity gate 按平台拉取、逐字节校验后装包。桌面 bundle 平台(darwin/windows)随 app 交付引擎;Linux 走 linux-browser SKU payload(cteno-cef-headless + libcef.so + icudtl.dat + locales/)。",
50
+ "platforms": [
51
+ "darwin-aarch64",
52
+ "darwin-x64",
53
+ "windows-x64",
54
+ "linux-x64"
55
+ ]
56
+ }
57
+ },
58
+ "permissions": {
59
+ "externalProcess": [
60
+ "cteno-cef-headless"
61
+ ],
62
+ "network": true,
63
+ "filesystem": []
64
+ }
65
+ }
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: Cteno Browser
3
+ description: Daemon-owned CEF browser engine with the structured `browser` tool (open / semantic-snapshot / semantic-step / js / screenshot / tabs). Works headless (no GUI) and with the GUI pane as a live viewer.
4
+ ---
5
+
6
+ # Cteno Browser
7
+
8
+ 浏览器能力由 daemon-owned CEF 引擎提供,`browser` 是 runtime-native tool——直接调用它,不需要激活本 Skill 才能用。本文档只补充插件级事实:
9
+
10
+ - **引擎归属**:引擎跑在 daemon(`cteno-cef-headless`),GUI pane 只是它的 viewer。无 GUI(Linux headless with browser)时 `browser` 工具照常可用,只是 `visible_pane: false`。
11
+ - **pane Tab 与 agent target 分层**:pane 的 Web/App 混合 tab 世界由 daemon 以客户端生成的字符串 `tabId` 统一记账(顺序、活动指针、`Opening → Live → Closing`);GUI 只投影宿主。`targetId` 仍是 Web tab 的可替换引擎属性,也是 agent `browser` 工具必须显式携带的页面身份;`browser tabs` 继续只列引擎 targets(含 hidden),永不暴露 App tabs。pane chrome 的 `close-tab {tabId}` 同步让行消失并立即返回,物理 target 销毁在后台分级完成。
12
+ - **观察-行动-验证**:页面交互固定走 `open/tabs` 取得 `targetId` → `semantic-snapshot` → `semantic-step`(同一 `targetId` + `backendNodeId` 定位)→ 检查 `delta`、正交 `outcome` 与 `observations` → 核验页面业务状态。凡观察、驱动、导航、截图或关闭既有 tab 的命令都必须显式携带 `targetId`;缺失、stale、跨 session 或 unowned 时 fail-closed,绝不退回 ambient active tab。`success=true` 只表示浏览器命令执行完成;上传的 `actionEvidence.deliveryObserved=true` / `delivery.count>0` 只表示文件到达页面 input,都不代表网站接受了操作。
13
+ - **原子与恢复合同**:需要虚拟时间一致性时,`semantic-step` 同时传 `requireAtomic:true` 与 `virtualTimeBudgetMs>0`;能力不可用时必须在动作投递前失败,不静默降级。受阻动作返回 `success:false` 和 `outcome.blocker`(`kind`、`retrySafe`、`requiresFreshSnapshot`、`recoveryActions`),调用方按建议重新观察/检查 blocker,不解析错误字符串。`outcome.execution/delivery/page/network/semanticEffect/businessAcceptance` 是正交事实,兼容 `status` 只作摘要。
14
+ - **上传事务证据**:`upload-file` 优先遵循可见 uploader 的 `label for`、包裹 label、`aria-controls` 等显式关联;多个显式 file input 时拒绝猜测。`actionEvidence` 返回 `phase`、`deliveryMethod`、`attemptedMethods`、`deliveryObserved`、`pageProcessingObserved`、`retrySafe` 与 `businessAcceptance`;只有页面/管理页业务后置条件可把 acceptance 判为成功。
15
+ - **截图双消费**:`browser screenshot` 保留稳定的 `data.path` / `image_path`,因此后续 `Read(path)` 和无视觉模型的 `describe_image` 路径不能移除;runtime-native 调用同时附加标准 `images`,让聊天记录保存 object attachment,视觉模型可在当前 turn 直接摄取。CLI stdout 仍保持 path-only,避免输出整张 base64。
16
+ - **异常立即冻结**:`outcome.status` 为 `page-rejected`、`page-error` 或 `no-observable-change`,以及 delta 与预期不符时,立即停止当前业务路径,保存 snapshot/step 证据并读取 `page-observations` 和页面状态。不得连续重试,也不得改用 JS、CDP、selector 或坐标换招硬闯;先定位 browser host、semantic/runtime、站点业务或服务端中的责任层。
17
+ - **查询能力(kernel observation)**:`semantic-snapshot mode=compact` 走渲染进程内的 relevance 查询(predicate + budget),只回传 top-N 可交互节点,传输体积由 budget 决定而非页面节点数。密集表单/菜单用 `nodeBudget`(默认 12)调大 N;旧 framework 无 captureObservation 时自动回落 stitched 路径(`consistency.level = "best-effort"`)。目标节点不在 top-N 里时用 `semantic-search`(`query`/`roles`/`tags`/`actionable`/`clickable`/`editable`/`disabled`/`viewportOnly` 过滤 + `offset`/`limit` 分页),在同一份已缓存快照的候选池里检索,不需要重抓或读整份 artifact。
18
+ - **业务结果单独验收**:动作完成后重新 snapshot,确认上传进度、处理完成、表单校验或目标状态确实出现;发布、提交和修改还必须回内容管理页或详情页核对唯一目标、明确状态与实际素材,不能用按钮消失、跳转、toast 或表单预览代替。
19
+ - **hidden 模式**:`hidden: true` 走独立离屏 scope,不抢占用户可见 pane;一条 hidden 流程内每次调用都必须带 `hidden: true`。
20
+ - **诊断范围**:`semantic-step` 返回当前动作窗口的 observations;`page-observations` 返回的 `scope` 会说明覆盖范围。失败请求、JS 异常、console error 或 `truncated=true` 都必须按原证据解释,不能把服务端拒绝误判为点击失败,也不能把诊断日志直接当作业务错误。
21
+ - **登录态读数**:`browser cookies <origin> [--names a,b,c]` 回答「这个站还登录着吗、还剩多久」。它读的是引擎 profile 的 cookie jar(browser 级连接),**不导航、不需要拥有标签页**——判断是否需要访问站点这件事本身不该是一次访问。只回元数据(域/路径/过期/secure/httpOnly)与关键 cookie 里**最早**的那个 deadline,**永不返回 cookie 值**,所以可以安全地写日志、给模型、显示给用户。真要值请显式走 `cdp Network.getCookies`。不带 `--names` 是「发现模式」:只列出有什么,**不给 deadline 也不给判决**——没人指名哪个 cookie 承载会话时,整罐里最早的那个只是某个无关分析 cookie 的寿命。
22
+ - **token 登录看 storage**:`browser storage <origin> [--names a,b,c]`。现代 SPA 的登录**根本不在 cookie 里**(实测:已登录的 DeepSeek 控制台,4 个 cookie 全是 WAF 会话/设备指纹/缓存键,凭据是 `localStorage.userToken`),对这种站点 `cookies` 会稳定报「未登录」。web storage 按 origin 分区、拿不到无页面的读法,所以它借用 origin 开一个一次性 tab;但 document 在 `Fetch` 的 Request 阶段被本地 fulfil,**零字节到达站点**——是一次借用了 origin 身份的本地读取,不是一次访问。同样只回 key 的存在性/大小与 JWT 解出的 `exp`,**永不返回 token 值**;不透明 token 没有自带过期时回 `expiryUnknown`,那不是错误,是常态。
23
+ - **等网页回复**:在网页上发完消息要等对方回复时,`watch-notifications {targetId}` 把该 tab 的网页通知临时挂到本会话,然后结束本轮。对方回复会作为一条消息推进来;这时读 tab,再用 `semantic-step` 在输入框回复。挂载默认 120 分钟(`minutes` ≤1440),到期或 tab 关闭/被回收都会收到「已结束」消息,之后改为重新 `open` 该 url 查看。事情办完就 `stop:true`。不需要另外轮询;只有担心页面不发通知时,才可选设一个一次性 loop。
24
+ - **交还给人**:只有 agent 能交还方向盘——`driver mode=user reason=assist`(临时求助;随后结束本轮)或 `reason=done`(完工退出)。不存在第三态。交还后告诉用户去这个 session 的浏览器面板处理;如果某个已启用插件的说明提供了交接链接(例如 remote-control 的 `cteno-remote link`,可让人从手机接手),按插件说明发出那条链接。不要自己拼链接。
25
+ - **不用判断人在哪**:同一条接手链接,在电脑上点 = 打开 pane 到那个 tab;在手机上点 = 本机自动签发一次性 RustDesk 远程并把 Cteno 窗口/该 tab 摆到前台,agent 重新接手后自动 revoke。agent 不要主动打开 pane,也不要为浏览器接手自己跑 `cteno-remote grant`。手机端提示远程插件不可用时,再 `cteno-host-cli plugin install remote-control` + `plugin enable remote-control`。
26
+ - **插件开关即能力开关**:禁用本插件后 `browser-*` RPC 与 runtime browser 工具统一报 `browser_capability_unavailable`,不是 unknown method。
27
+
28
+ CLI 调试入口:`cteno-host-cli browser <command>`(与工具同一命令面,详见 `cteno-host-cli browser help`)。整机生命周期验收/运维用 `cteno-host-cli browser close-browser` 协作 drain daemon-owned 引擎;它保留 `close`/`stop` 旧别名兼容,但新脚本应使用语义明确的 `close-browser`。