Repository navigation
✨ 外部接入:支持 sctl 触发后台/定时脚本运行并读取脚本运行日志 - #1825
Merged
Merged
Conversation
原用例按帧上限构造 4 MiB 错误文本与 2 MiB 日志,单跑约 40ms,但在 CI 覆盖率与分片负载下 达 280–630ms,超出 340ms 单测预算(PR #1825 CI shard 2/2)。2 MiB 日志本身不区分实现对错, 改为错误文本刚超过 16384 码元并直接断言截断结果;去掉截断时该用例仍失败。
CodFrm
added a commit
that referenced
this pull request
Nov 5, 2026
…间接办法 - dev-loop.md:新增 sctl run / sctl logs 用法与注意事项(只限后台/定时脚本、exit 0 看状态、不经审批但有真实副作用、 手动运行不排重试、debug 级 GM_log 不入库、与编辑器运行或 cron 交叠可能误认),旧 sctl / 旧脚本猫的识别与回退 - background.md:删去无代码依据的「编辑器运行不同步值、菜单不触发」,改为与 sctl run 同一通路 - SKILL.md、api.md:GM_log 的 debug 级别在正式版不入库 - README 已知限制:依赖 scriptscat/sctl#17 与 #1825 的版本
3 tasks
stopScript 只清掉旧运行的计时器并移出 execScriptMap,旧运行的 Promise 之后结束时仍会发布 COMPLETE/ERROR,CATRetryError 还会把已停用的定时脚本加回重试队列。运行状态不带运行 id, 脚本列表与外部接入 scripts.run 会把旧运行的终态当成新运行的。现在只有仍登记为该脚本当前 运行的 exec 才发布终态与安排重试;旧运行的结果照常写日志。
按 sctl spec(729a663)修订后的约定: - 脚本 @run-in 与本实例环境不符时 sandbox 不会运行它,scripts.run 现在立即返回 INVALID_REQUEST 而不是等满超时报 running。判断抽成 sandbox/run_in.ts 的 backgroundRunInAllowed,sandbox 与外部接入 共用(含 Firefox spanning 例外),外部接入按 sandbox 同样合并 selfMetadata。 - 一个截止时间从收到请求起算,覆盖排队与等待,排队调用不再可能耗时“上一次超时 + 本次超时”; wait=false 不排队。排队耗尽截止时间时照样触发并返回 running。 - $/cancelRequest 与断开连接现在也送达进行中的 scripts.run:尚未触发的排队运行被放弃 (OPERATION_EXPIRED,不再触发),正在等待的调用立即结束并释放订阅与计时器;审批取消语义不变。 - 日志标签含 BigInt 或循环引用时不再让整次查询 INTERNAL_ERROR:BigInt 取十进制文本, 无法编码的值返回 "[unencodable]";运行错误文本同样处理。 - 日志改用 LoggerDAO.walkNewestFirst 沿 createtime 索引从新到旧游标读取,凑够 limit/字节预算即停, 不再把 since 之后全部脚本的日志载入内存再排序;同毫秒按 id 先后由索引顺序保证。
run_in.ts 更名为 utils.ts;runtime.execScript 内联的 once 门控(按 once 所在字段比对 lastruntime) 提成纯函数 isOnceRunDone,行为不变。两者此前分别只被间接覆盖与完全没有测试,补上 utils.test.ts。
CodFrm
added a commit
that referenced
this pull request
Nov 5, 2026
…#1824) * 📝 新增 scriptcat-userscript Skill:指导 AI 编码代理编写页面/后台/定时脚本,并通过 sctl 安装与验证 - SKILL.md:选脚本类型、骨架、按权限/时机/存储/后台分组的易错规则,有无 sctl 两条验证路径 - references/api.md:按 script_compat.ts 的 grant 白名单列出已实现的 GM/CAT API 与和 TM 的差异 - references/background.md:Promise 生命周期、CATRetryError 重试、cron 与 once 扩展、日志 - references/userconfig.md:==UserConfig== 格式(类型推断按 UserConfigPanel 实际行为) - references/dev-loop.md:sctl install → 后台标签页 reload → debug console / page eval 验证 → 重装或 sctl edit 迭代; 已在真实浏览器实测:用户脚本 console 可见、整文件 edit 生效、同 name+namespace 重装原地更新(uuid 不变) * 📝 修正 scriptcat-userscript Skill 中与运行时不符的描述 - @grant none 下只有 GM_info/GM.info,没有 unsafeWindow(exec_script.ts 只注入 { GM, GM_info },unsafeWindow 仅在沙盒上下文 create_context.ts 设置) - cron 的 once 以「开始运行」计周期,失败的运行也占用本周期(lastruntime 在运行开始与每次状态更新时写入),失败重跑须用 CATRetryError - GM_foo 与 GM.foo 互通仅在名字完全一致时成立:@grant GM_xmlhttpRequest 不会暴露 GM.xmlHttpRequest(getGrantCandidates 只换前缀、区分大小写) - 每一行 @crontab 都会在运行时注册,安装校验和脚本列表只看第一行,任一行非法会停掉全部任务,因此改为「只写一行」 - 脚本体被包在 async 函数里,返回值总是 Promise;不 return Promise 时同步部分结束即记为完成,不会出现 "return not promise" 警告 - 解析 cron 的是 npm 包 cron(非 node-cron);UserConfig 的 min 不作用于 text;sctl grep 在多实例时同样报 SCRIPTCAT_AMBIGUOUS;page reload 已等待 load * 📝 scriptcat-userscript Skill 补充写脚本的方法与常用写法 - dev-loop.md:写之前用 sctl 侦察页面(snapshot/eval 试选择器与逻辑,debug network/request 找站点接口,鉴权信息不外泄) - dev-loop.md:用 @require file:/// 壳脚本迭代本地文件,审批一次即可(每次页面加载重读文件,需「允许访问文件网址」) - dev-loop.md:后台/定时脚本的可观测办法(重装即重跑 @background、测试时临时每分钟 cron、共享 @storageName 的探针页面脚本) - 新增 references/patterns.md:等待元素、SPA 路由切换、带记忆的菜单开关、跨域请求、样式、多语言、带重试的后台运行 - SKILL.md 加入口链接,README 结构表与已知限制同步 * ✨ scriptcat-userscript Skill 附带静态检查与壳脚本生成工具 - scripts/check.mjs:零依赖静态检查,按运行时规则报告未声明 grant 的 API 调用(含 depend 一并注入的 API)、@grant none 下用 GM API/unsafeWindow、none 与其他 grant 混写、页面脚本缺 @match/@include、后台脚本用 GM_getTab 系列、cron 字段/once/空格写法错误;警告未知 grant、后台脚本无顶层 return、多行 @crontab、@run-at/@early-start 不生效、请求域名不在 @connect 也不在 @match 下;有 error 时退出码 1 - scripts/stub.mjs:从本地脚本生成 @require file:/// 壳脚本(保留头部与 UserConfig),配合本地热重载 - check.test.mjs 对照 SUPPORTED_GRANTS 守卫内置 grant 清单;vitest 的 scripts 项目纳入 skills/**/*.test.mjs - SKILL.md、dev-loop.md、README 补充用法 * 📝 scriptcat-userscript Skill:后台/定时脚本优先用 sctl run / sctl logs 验证,旧版本退回间接办法 - dev-loop.md:新增 sctl run / sctl logs 用法与注意事项(只限后台/定时脚本、exit 0 看状态、不经审批但有真实副作用、 手动运行不排重试、debug 级 GM_log 不入库、与编辑器运行或 cron 交叠可能误认),旧 sctl / 旧脚本猫的识别与回退 - background.md:删去无代码依据的「编辑器运行不同步值、菜单不触发」,改为与 sctl run 同一通路 - SKILL.md、api.md:GM_log 的 debug 级别在正式版不入库 - README 已知限制:依赖 scriptscat/sctl#17 与 #1825 的版本 * 🐛 scriptcat-userscript check.mjs:修复对象键误报、关键字后正则漏报与 @connect 同域误判 - 对象字面量的键(`{ GM_getValue: 1 }`)不再算 API 调用;三元表达式里的仍算 - return/typeof/case/throw/in/of/void/delete/new 等关键字后的 `/` 按正则开头处理,正则里的引号不再吞掉同一行后面的调用 - @match 带通配子域或有多个域名时,请求其中一个域名不再视为同域(运行时只放行与页面完全相同的域名,见 getConnectMatched) - connect-missing 文案区分:脚本已有 @connect 时 GM_xmlhttpRequest 会被拒绝,没有任何 @connect 时询问用户;仍为 warn,因为检查只看 URL 字面量,无法确定它是否真被请求 - 某个文件读不了时继续检查其余文件,最后以退出码 2 结束 - check/stub 命令行用例真实 spawn node,按 scripts/check-i18n.test.mjs 的做法单独设超时,避免满载时超出 340ms 单测预算 * 📝 scriptcat-userscript Skill:按代码订正运行结果查看位置、@connect 拒绝规则与若干易错点 - 脚本列表只显示运行状态(运行中/错误/已停止)与定时脚本下次运行时间,GM_log 与错误信息改指向日志页(按 name/uuid 过滤)或 sctl logs - @connect:脚本已有 @connect 时未列入的域名被 GM_xmlhttpRequest 直接拒绝(之前允许过的除外),只有完全没有 @connect 才询问;native GM_download 总是询问;仅与页面完全相同的域名免声明 - CATRetryError 在 @grant none 下不存在;没有任何 @grant 行的脚本同样运行在沙盒里 - 同 @name/@namespace 不能在页面脚本与后台/定时脚本之间切换(类型不符被拒),临时页面脚本需另起名字 - GM_addElement 注入 <script> 在 Firefox 仍受页面 CSP 约束;urlchange 依赖 Navigation API,onurlchange === null 只说明授权生效 - dev-loop:补 get/run/logs 的 --scriptcat 行为、sctl run 的实际状态行与 JSON 字段(含 truncated)、@run-in 不符返回 INVALID_REQUEST;壳脚本示例的说明移到注释行;后台壳脚本可用 sctl run / 编辑器运行免审批重读本地 @require
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checklist / 检查清单
N/A — 无关联 issue。测试与真实验证见下方「验证」;尚未经过人工审查。
背景
sctl 无法触发后台/定时脚本运行,也读不到脚本的运行日志:这两类脚本跑在 offscreen/sandbox 里,不是网页,sctl 也不能 attach 扩展页面。所以 AI 代理写完这类脚本后没法自己验证(见 #1824 的 scriptcat-userscript Skill)。sctl 侧新增了
sctl run/sctl logs和 MCPscripts_run/scripts_logs(配对 PR:scriptscat/sctl#17)。本 PR 让脚本猫的外部接入 bridge 应答对应的两个新 JSON-RPC 方法。Spec(在 sctl 仓库):
docs/specs/2026-11-04-script-run-logs.md。本次改动
scripts.run:复用脚本列表/编辑器“运行”的同一通路(RuntimeService.runScript,execOnce),只允许后台和定时脚本(启用与否不影响)。complete/error/running(超时,默认 60 秒,上限 240 秒)和本次产生的日志。wait:false时立即返回。scripts.logs:按 uuid 读取LoggerDAO日志,支持 since(默认 1 小时)、severity、text、limit(默认 100,上限 1000)。truncated;按帧大小上限截断。logs调用都会新增一条。threat-model.md已记录残余风险),并照常写入外部接入审计(不含日志正文)。external_access/generated/由 sctlmake protocol-sync-scriptcat生成。schemaVersion保持 1.0.0:握手要求版本完全相等,升版本会让旧版连不上。实现考虑
scriptRunStatus没有运行 id。重跑时会先发旧一次运行的 COMPLETE,再发新一次的 RUNNING。所以必须先看到本轮的 RUNNING,才认后面的终态。已知限制
run返回的本次日志里可能缺这一行。GM_log(..., "debug")不会入库,run/logs看不到它。这是原有行为,本 PR 没有改。建议审查重点
external_access/run_logs.ts中等待本轮 RUNNING → 终态的状态机,以及每条退出路径上的订阅和定时器释放。关联
SCRIPTCAT_REF指向本分支的373a117c(生成文件在1570587c中未变)。合并顺序:先合本 PR,再合 sctl PR。sctl run/sctl logs。验证
base
6c0459b8…headbdc914d1。改动只在src/app/service/service_worker/external_access/(含generated/)和service_worker/index.ts,共 7 个文件。pnpm exec vitest run --no-coverage src/app/service/service_worker/external_access src/app/service/service_worker/index.test.ts:9 个文件、187 个用例通过。新增用例先红后绿;另做过变异检查:分别去掉 RUNNING 守卫、审计排除、排序、错误转字符串,相应用例都会失败。pnpm exec tsc --noEmit、改动文件的 eslint 和 prettier:通过。origin/main6c0459b8上就不稳定地大面积失败(跨文件 mock 泄漏、超时),失败文件单独跑都能通过,CI 上同一提交是绿的。127.0.0.1:18643的 daemon,覆盖 spec 的 18 项要求,全部通过:--no-wait;INVALID_REQUEST,未知 uuid 返回NOT_FOUND;6c0459b8构建的旧版收到METHOD_NOT_FOUND和升级提示,已有命令照常可用。审查修复(2026-11-05)
又做了一轮独立审查,确认的问题都已修复,先写失败用例再改:
4f7ccc1bsandbox:脚本被重跑或停用后,旧运行晚到的结束状态不再覆盖当前运行的状态,也不再给它安排重试。这个问题原本就存在,脚本列表上的运行状态同样受影响;scripts.run依赖这个状态,所以在这里一并修复。3b0038f5:@run-in与当前环境不符时,立即返回INVALID_REQUEST,不触发运行,判断逻辑与 sandbox 共用sandbox/utils.ts。wait:false不再排队。$/cancelRequest或断开连接时,放弃还在排队的运行。LoggerDAO.walkNewestFirst从最新往回读,够数就停,不再整表读取。729a663)。验证(head
bdc914d1):相关测试 216/216 通过,
tsc与 eslint 均为 exit 0;每项都做了变异检查。真实浏览器复测,三项都通过:
run/logs正常;--no-wait启动运行 A,1 秒后启动运行 B;A 等待的本地延迟请求返回后,B 得到的仍是自己的结束结果,A 晚到的结束只记进日志。bdc914d1♻️ 新增sandbox/utils.ts:run_in.ts并入其中;runtime.execScript里内联的 once 周期门控提成纯函数isOnceRunDone,行为不变。补了utils.test.ts(once 门控以前没有测试),做过变异检查。相关测试 225/225 通过,tsc和 eslint 均为 exit 0。