diff --git a/.agents/skills/comet-any/SKILL.md b/.agents/skills/comet-any/SKILL.md new file mode 100644 index 0000000..2e9bc3f --- /dev/null +++ b/.agents/skills/comet-any/SKILL.md @@ -0,0 +1,118 @@ +--- +name: comet-any +description: "仅在用户明确调用 /comet-any,或明确要求定制 /comet-classic 五阶段流程、创建或升级由 Comet Creator 管理的 workflow Skill 时使用;不要用于一般 Skill 的编写、整理或评审。" +--- + +# Comet Any - Skill Creator + +`/comet-any` 是 Comet 的 Skill 创建向导。用户只需要描述想要的工作流;本 Skill 负责读取真实 Skill、提出方案、等待确认、生成可验证的 Comet-native Skill Bundle,并通过内部 CLI 完成 eval、review、publish readiness 和安装预览。 + +普通用户第一层只看到三种起点: + +- `基于 /comet-classic 的五阶段定制`:覆盖 `open / design / build / verify / archive` 五阶段的 Skill 编排,但不修改 `/comet-classic` 永久入口本身。 +- `创建全新 workflow Skill`:从目标和候选 Skills 生成新的 `workflow-kernel`。 +- `整理已有 Skill`:读取已有 Skill,补齐 Workflow Node、Skill Binding、Output Schema、Guardrail、Handoff、eval 和 readiness。 + +后端 Bundle、Factory、composition 仍是内部审计词;不要把它们作为普通用户的第一屏概念。 + +## 核心模型 + +所有路径都必须编译到同一种 Workflow Contract: + +- `Workflow Node`:流程中的可恢复节点,例如 `open`、`design`、`plan`、`execute`、`subagent-execute`、`review`、`verify`、`archive`。 +- `Node Responsibility`:该 Node 在 Agent workflow 中承担的职责,用来解释它为什么存在、需要产出什么、能否替换。 +- `Skill Binding`:某个 Node 的实现 Skill 或辅助 Skill。 +- `Required Skill Call`:要求 Node 内必须调用某个 Skill,不替换 Node implementation。例如 `execute` 和 `subagent-execute` 必须调用 `elementui`,`review` 必须调用 `whitebox-code-standard`。 +- `Output Schema`:Node 必须产出的文件、状态或 evidence。Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。脚本、eval、readiness 只依赖挂到 Node 的 Output Schema,不依赖 Skill 名称。 +- `Guardrail`:阻断或放行 Node 推进的检查。 +- `Handoff`:子代理或跨 Node 交接时必须带回的 evidence。 +- `workflow-protocol.json`:生成包的唯一运行事实源,kind 为 `comet-five-phase-overlay` 或 `workflow-kernel`。 + +## 受保护边界 + +`comet-five-phase-overlay` 保留 Comet Classic 五阶段主流程和 `.comet.yaml` 状态语义。普通模式下: + +- `comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`;没有 active change 或多个 active changes 时必须阻塞并请用户选择。 +- 不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态。Bundle 草稿、eval evidence 和 publish readiness 可以有自己的证据文件,但不能替代 `.comet.yaml`。 +- `control` Node 不允许 override:`open`、`execute`、`verify`、`archive`。 +- `producer` Node 可以 override:`design`、`plan`,但必须满足对应 Output Schema。 +- `handoff` 和 `guardrail` Node 可以 require / augment。 +- 用户坚持替换 control Node 时,改走高级 `workflow-kernel`,并要求重新声明 state、Output Schema 和 Guardrail。 +- 所有 Node 都必须用 responsibility 说明职责,不使用内部坐标作为用户理解流程的方式。 + +## 工作步骤 + +1. 恢复现有状态:先运行 `comet creator guide --project . --json`,展示恢复摘要和下一步。 +2. 读取项目偏好:读取 `.comet/skill-preferences.yaml`,用 `comet creator candidates --json` 发现真实本地 Skill,再用 `comet skill show --json` 读取候选的真实内容与 hash。不得只按名字推测能力。 +3. 生成方案:把用户目标表达为 Workflow Nodes、Skill Bindings、Output Schemas、Guardrails、Handoffs 和 Evidence。 +4. 展示确认页:说明每个 Node 的职责、绑定 Skill、Required Skill Call、Output Schema、可执行披露和 readiness 影响。确认页必须为每个新增 binding 或 schema 显示 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +5. 等待用户确认:未确认前不得写 Bundle draft;存在 missing / ambiguous Skill 时必须暂停。 +6. 初始化后端状态:确认后调用 `comet creator init --file --confirmed-proposal --json`。 +7. 运行创作管线并生成 Bundle:先运行 `comet creator authoring-plan --depth quick|full --json` 取得 lane DAG。按 DAG 派发 lane——wave1(`script`、`reference`、`pause-points`)在支持子代理的平台可并发(否则按依赖顺序内联),wave2(`workflow-entry`、`skill-core`)在 script 契约之后,`skill-review` 作为汇聚 barrier。每个 lane 的产出用 `comet creator authoring-record --lane --file --json` 记录(经 schema 校验;BLOCKED/NEEDS_CONTEXT 会被拒绝)。随后运行 `comet creator generate --json`:把记录的内容叶子草稿(entry/node SKILL.md、decision-points、recovery)合并进包,而确定性脊梁(protocol/scripts/manifest)保持模板化,并渲染真实审查证据。产出 entry Skill、Node Skills、`reference/workflow-protocol.json`、六个 scripts、rules、hooks 与 `comet/eval.yaml`。 +8. 验证:展示 quick/full eval 工作量,运行或记录当前 draft hash 的 eval evidence;失败、skip 或证据 hash 过期时不得进入 ready。 +9. Review / readiness:读取 `comet publish review --platform --json`,展示 `Readiness:`、`Blockers:`、`Warnings:`、`Evidence:`。 +10. Publish / install preview:人工批准后才能 publish;安装前必须先运行 preview,并展示 `No files were written`。 + +## 方案示例 + +组件库和白盒审查场景应生成类似 plan: + +```json +{ + "goal": "基于 /comet-classic 的五阶段定制,要求组件库和白盒审查。", + "skillCreatorIntent": "customize-comet", + "workflow": { + "kind": "comet-five-phase-overlay", + "name": "team-comet", + "goal": "要求组件库和白盒审查。", + "nodes": { + "execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "reason": "Use project component library during direct implementation." + } + ] + }, + "subagent-execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "scope": "handoff" + } + ] + }, + "review": { + "requiredSkillCalls": [ + { + "skill": "whitebox-code-standard", + "scope": "review" + } + ] + } + } + } +} +``` + +## 硬性规则 + +- 必须先展示方案确认页,再生成。 +- 确认页必须为每个新增 binding 或 schema 显示 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +- Required Skill Call 不替换 Node implementation。 +- producer override 必须声明 `satisfies` 的 Output Schema。 +- Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。 +- control Node 普通模式不得 override。 +- eval、review、publish readiness 必须读取同一份 `workflow-protocol.json`。 +- readiness blockers 必须阻止 publish:缺少当前 draft hash 的 eval evidence、人工 approval、required capability 或 executable disclosure 任一项都不能进入 ready。 +- 子代理 Handoff 必须要求子代理加载 Required Skill Call 并回传 evidence。 +- 脚本只读取 protocol 和 state,不把 Skill 名称当成校验依据。 +- 安装前必须询问用户,不得自动安装。 + +## 参考资料 + +- `comet-any/reference/authoring-protocol.json` +- `comet-any/reference/authored-zone-example.md` +- `comet-any/reference/bundle-authoring.md` +- `comet-any/reference/authoring-subagents.md` +- `comet-any/reference/eval-provider.md` diff --git a/.agents/skills/comet-any/reference/authored-zone-example.md b/.agents/skills/comet-any/reference/authored-zone-example.md new file mode 100644 index 0000000..0bd453b --- /dev/null +++ b/.agents/skills/comet-any/reference/authored-zone-example.md @@ -0,0 +1,105 @@ +# Authored 区质量标尺(样例) + +这是你编写的 **Authored 区**(entry 的 `## Decision Core`、node 的 `## Guidance`)的具体质量标尺。生成器把你的 Authored 区组装到确定性 **Auto 区**(frontmatter、路由表、Entry/Exit Check、证据格式、Recovery)之上。**你只写 Authored 区**。请按本样例校准,而不是写一行套话。 + +## "Auto" 与 "Authored" 的含义 + +- **Auto 区(模板,不要重写)**:不变的控制面。node 的 Auto 区包括 `## Node Goal`、`## Entry Check`、`## Skill Implementation`、`## Required Skill Calls`、`## Output Schemas`、`## Evidence Record`、`## Guardrails`、`## Exit Check`、`## Recovery`。entry 的 Auto 区包括 `## Workflow Nodes`、`## Skill Bindings`、`## Guardrails And Evidence`、`## Runtime And Recovery`。 +- **Authored 区(你写)**:Auto 区无法得知的领域决策。这是让 Skill "好用"而非"只是能跑"的关键。 + +## 样例:`substance` 节点的 Guidance 区(workflow-kernel) + +下面是 research-writer workflow 中"Research"生产者节点的 `## Guidance` 正文。注意它是**决策内容**:前提、有序的领域步骤、超出机械 Exit Check 的完成判断、Red flags。它按名引用绑定的 Skill,但不复制其正文。 + +```markdown +## Prerequisites + +- entry 的 Decision Core 已确认研究主题与范围。 +- `research-skill` 在项目 Skill 池中可解析;若缺失,先停下询问用户,不要临时替代。 + +## Steps + +1. 加载 `research-skill` 并按其发现方法处理已确认主题。当项目 Skill 定义了特定来源顺序时,不要用通用网络搜索替代。 +2. 按优先级收集来源;为每个来源记录来源、日期和你要复用的论点。不通过项目可信度门槛的来源应直接剔除,而不是标注为"偏弱"。 +3. 把发现提炼到 `notes/*.md` 笔记文件——每条独立论点一份,含逐字引用与来源指针。综合写在 writer 节点,不在这里。 +4. 记录 `research.notes.v1` 的 `summary` evidence:一段提炼 + 产出的笔记数量。 + +## Completion reasoning + +本节点完成当且仅当两条同时成立:(a) 已记录 `summary` evidence;(b) 至少一个 artifact 匹配 `notes/*.md`。不要仅仅因为步骤清单走完就退出——如果相对主题范围笔记仍偏稀疏,应继续研究而非宣布完成。Exit Check 脚本会机械地强制 artifact + evidence 要求;你的职责是判断研究是否真正充分。 + +## Red flags + +- 记录了 `summary` 却没有产出任何 `notes/*.md` 就退出(guardrail 会阻塞——不要试图绕过)。 +- 把来源原文复制进笔记却不加引用标记或来源指针。 +- 某来源仍"待核实"就推进到 Write 节点——核实属于本节点。 +- 对需要多视角的主题,仅凭单一来源就认为充分。 +``` + +用 `###` 子标题,使其嵌套在 `## Guidance` 之下。上述四段(Prerequisites / Steps / Completion reasoning / Red flags)是 `substance` 节点的预期形态。 + +## 样例:entry Decision Core(workflow-entry) + +下面是 entry SKILL.md 的 `## Decision Core` 正文。entry 是**每次调用最先读取的文件**——一个薄 Decision Core("跑 next,照做")会让整个 Skill 感觉机械。一个富 Decision Core 是 comet 级 Skill "好用"的核心。 + +Decision Core 应建模 Auto 区不处理的三件事:(1) **语义化当前节点检测**(如何判断用户在哪个 Node,而非只看脚本输出),(2) **resume 与 drift 规则**(上下文恢复或状态与文件冲突时怎么办),(3) **决策点与 Red flags**(何时暂停等用户,以及什么是假进展)。 + +```markdown +### 自动节点检测 + +**Step 0:确定当前节点与意图** + +1. 检查 workflow protocol 的有序 Node 列表。第一个未完成(无 Exit evidence 记录)的 Node 是候选当前节点。 +2. 若用户描述的工作明显属于更后面的 Node(如"验证结果"但研究尚未完成),暂停并说明:前序 Node 必须先完成。不要跳过。 +3. 若用户描述的工作属于已标记完成的更早 Node,视为纠正——重置该 Node 的完成状态并重新进入。 + +**Step 1:读取 workflow 状态** + +运行 `node "$WORKFLOW_STATE" status` 确认检测到的节点。若脚本的 `NEXT:` 输出与文件证据冲突(如脚本说 DONE 但无 artifact),以文件为准,先纠正状态再继续。 + +**Resume 规则**: +- 每次上下文恢复,重新执行 Step 0 和 Step 1。不要信任对话历史做节点检测。 +- 若状态显示某 Node 已完成但预期 artifact 缺失,视为未完成并重新进入。 +- 若用户在某个 Node 中途恢复但话题变了,确认是继续当前 Node 还是开始新的。 + +### 决策分类与决策点 + +先分类再行动:有两个或以上会改变范围、行为、风险接受或不可逆结果的合法选项才是用户决策;唯一安全下一步直接执行;缺少依赖、状态损坏或无法继续的 guard 失败是停止条件;`NEXT: manual` 只是交还控制权。只有第一类必须暂停。 + +| 情况 | 处理 | +|------|------| +| 首次调用且主题/范围明确 | 自动初始化状态并进入第一个 Node;不得为了确认已知信息而停顿 | +| 主题、范围或目标 Node 存在两个以上互斥且合法的解释 | 合并为一个问题,让用户选择;不要猜测 | +| Node 需要用户确认输出才能推进 | 记录 evidence 后停下;等待明确确认 | +| WARNING/偏差接受或不可逆发布存在真实取舍 | 只展示当前可执行选项,并记录用户选择 | + +Node guard 失败时先自动读取证据并执行唯一安全修复;若缺少依赖或状态损坏导致无法继续,报告停止条件和恢复要求。只有恢复方式存在多个会改变范围或风险的合法选项时,才升级为上表中的用户决策。 + +### Red Flags + +| Agent 想法 | 实际风险 | +|-----------|---------| +| "首次调用都应该再确认一次" | 清晰输入不需要重复批准;只有互斥解释仍会改变范围时才询问。 | +| "脚本返回 NEXT: auto,应该立即加载下一个 Skill" | `NEXT: auto` 表示 Node 完成,不是跳过确认。检查下一个 Node 是否有决策点。 | +| "guard 失败,所以让用户决定怎么办" | 先自动诊断并执行唯一安全修复;无合法动作时报告停止条件,不要发明选项。 | +| "看起来和上次一样的话题,从上次断点继续" | 始终重新读取状态。对话记忆在上下文压缩后不可靠。 | +| "Exit Check 通过了,所以工作够好了" | Exit Check 是机械的。你的职责是判断检查之外的质量——稀疏笔记、浅层分析、缺失视角,脚本抓不到。 | +``` + +此样例以精简形式建模了 comet 的 Decision Core:语义检测(Step 0 读 Node 顺序,非只看脚本输出)、状态忠实(文件优先于过期状态)、resume 规则(每次恢复重新检测)、阻塞决策点(显式表格)、Red flags("想法 → 风险"模式,抓 agent 自欺)。 + +## substance 与 delegates + +- **substance** 节点(workflow-kernel):上面的样例就是标尺。必须有富 Guidance;缺失时该节点渲染为 `AUTHORING PENDING`,Bundle 不得 ready。 +- **delegates** 节点(comet-five-phase-overlay,委托给已安装的富 Skill):富执行内容由被委托 Skill 承载,**不要复制**。但如果该节点声明了 **Required Skill Calls**(如 execute 节点要求 `elementui`),请写一段聚焦的整合说明——在被委托流程的什么时机必须加载该 Skill、它补充什么 evidence——而不是泛泛一句"加载 X"。例如要求 `elementui` 的 delegates execute 节点: + +```markdown +本节点用 `comet-build` 执行。在其流程之外,每当改动涉及组件库时加载 `elementui`,并在记录 `required-skill:execute.elementui` 检查前确认改动使用了项目认可的组件。不要重复实现 `comet-build` 已做的事。 +``` + +## 反模式(不要写) + +- 一行式 Guidance,如"运行本节点并记录 evidence。"——Auto 区已隐含此意,毫无增量。 +- 整段复制被绑定 Skill 的正文。 +- 重述路由表或 Output Schema 列表(Auto 区已有)。 +- substance 节点没有 `### Red flags`——Red flags 是大部分真实价值所在。 diff --git a/.agents/skills/comet-any/reference/authoring-subagents.md b/.agents/skills/comet-any/reference/authoring-subagents.md new file mode 100644 index 0000000..842ac01 --- /dev/null +++ b/.agents/skills/comet-any/reference/authoring-subagents.md @@ -0,0 +1,107 @@ +# Authoring Subagents 总览 + +## 核心原则 + +`/comet-any` 的创作成果应由平台原生 subagent 分工产出,再交给主会话组装和后端 CLI 记录状态。 +Claude Code、Codex、Gemini、Copilot 等平台的 subagent 调用方式不同,本参考只定义职责、输入和输出; +具体派发方式使用当前平台提供的原生 subagent 机制。 + +`reference/subagents/*.md` 是跨平台 lane brief;Claude Code custom agent 必须单独生成到平台 agent 资源,并带 `name`、`description`、`tools`、`model` frontmatter。不要把这些 portable lane brief 直接当作 platform-native custom agent 安装。 + +先读取本总览,再只把对应角色 brief 交给对应 subagent。不要把六个角色 brief 合并成一个大 prompt; +主会话负责保留全局上下文、汇总成果、调用 `comet bundle` 和 `comet publish`,subagent 只产出可审查草稿。 + +平台支持 subagent 时必须调度。没有平台 subagent 能力时,主会话可以使用同一份 brief 以内联方式完成, +但必须在用户可读摘要和 `reference/authoring-lanes.json` 中标记为 fallback。 + +所有 subagent 只返回 Markdown 成果和结构化审查结论,不得直接写入 Bundle state,不得执行候选 Skill 的脚本, +不得运行发布、安装或分发命令。CLI 状态仍由主会话维护。 + +## 角色 brief + +在用户确认 Skill Creator 方案后、运行 `comet creator generate` 或生成源码前,主会话按以下顺序读取并派发: + +1. 脚本作者 subagent:`comet-any/reference/subagents/script-author.md` +2. reference 作者 subagent:`comet-any/reference/subagents/reference-author.md` +3. workflow entry 作者 subagent:`comet-any/reference/subagents/workflow-entry-author.md` +4. Skill 核心作者 subagent:`comet-any/reference/subagents/skill-core-author.md` +5. 停顿点作者 subagent:`comet-any/reference/subagents/pause-points-author.md` +6. Skill 审查 subagent:`comet-any/reference/subagents/skill-reviewer.md` + +角色文件在本 Skill 内的相对路径是: + +- `reference/subagents/script-author.md` +- `reference/subagents/reference-author.md` +- `reference/subagents/workflow-entry-author.md` +- `reference/subagents/skill-core-author.md` +- `reference/subagents/pause-points-author.md` +- `reference/subagents/skill-reviewer.md` + +这些 subagent 的成果先落为可审查草稿,再进入 `reference/authoring-lanes.json`、`reference/skill-review.md` +和最终 Bundle draft。若任一 subagent 报告 blocking finding,必须停在草稿修复,不得继续 ready。 + +## 按 DAG 派发 + +上面的角色 brief 顺序只是创作 DAG 的线性展开,并非要求严格串行。权威 DAG 在 `reference/authoring-protocol.json` 与 `comet creator authoring-plan --depth quick|full --json`: + +- **wave1**(`script`、`reference`、`pause-points`):彼此无依赖。在支持子代理的平台上并发派发这三个。每个只拿到自己的角色 brief、通用输入和 protocol/resolved-skills 路径(文件交接,不共享历史)。 +- **wave2**(`workflow-entry`、`skill-core`):依赖 script 契约(`NEXT:`/`SKILL:` 输出)。仅在 script lane DONE 后开始。两者之间可并发。 +- **barrier**(`skill-review`):唯一同步点。仅在 wave1 与 wave2 全部 DONE 后运行;审查者必须读取所有 artifact 与 claim。 + +无论平台如何: + +- 编排遵循 DAG 依赖;只有 barrier 真正等待此前所有 lane。 +- 不支持子代理的平台,主会话按依赖顺序内联执行同样的 lane——语义完全一致,仅延迟不同。在 `reference/authoring-lanes.json` 中按 lane 记录 `dispatchMode: "subagent"` 或 `"inline"`。 +- Claude Code 可把某个 wave 的扇出委托给其 `Workflow` 工具作为可选加速器;这是实现选择,不是契约的一部分。契约是 protocol + schemas + DAG,任何平台都能解释。 +- 每个 lane 产出在下一个依赖 wave 开始前,必须经 `comet creator authoring-record --lane --file --json` 校验并记录。 + +## 通用输入 + +每个 subagent 都必须拿到同一组上下文: + +- 用户确认的目标、起点和语言。 +- `plan.json` 中的 `goal`、`workflow.kind`、`workflow.nodes`、`engineMode`、`runnerMode`,以及规范化后的 `workflow-protocol.json`。 +- `reference/resolved-skills.json` 或等价的真实 Skill 来源摘要。 +- `reference/workflow-protocol.json` 或即将写入该文件的 workflow protocol。 +- `/comet-classic` 定制场景下的受保护边界:`open / design / build / verify / archive`、`.comet.yaml`、decision point、verify-result-transition、archive-delta-sync。 +- 项目级偏好、缺失/歧义候选处理结果、偏离原因、scripts/hooks 可执行披露。 + +## 输出格式 + +每个 subagent 返回: + +```json +{ + "lane": "", + "artifacts": [ + { + "path": "reference/example.md", + "kind": "reference", + "content": "..." + } + ], + "claims": [ + { + "id": "reference:example", + "kind": "reference", + "paths": ["reference/example.md"], + "summary": "说明该成果保证了什么" + } + ], + "findings": [] +} +``` + +`claims` 是审查依据,不是装饰字段。缺少关键 claim 时,Skill 审查 subagent 必须阻塞。 + +## 派发注意事项 + +- 每次派发必须创建新的 subagent,不得继承主会话历史。主会话只提供该角色需要的 brief、输入路径和必要背景。 +- 必须显式指定 model;平台不支持 model 选择时,在 `reference/authoring-lanes.json` 记录为 platform-default。 +- 使用文件交接:主会话提供路径,不粘贴大段全文。通用输入、来源 Skill 摘要、草稿 artifact 和报告都应以路径交接。 +- 每个 subagent 只接收自己的角色 brief、通用输入和必要 artifact,不接收其他角色的完整 brief。 +- Skill 审查 subagent 必须在其他五个作者角色产出后运行,并读取所有 artifacts 与 claims。 +- 主会话可以要求某个角色返工,但返工结果仍必须回到 `reference/authoring-lanes.json` 和 `reference/skill-review.md`。 +- subagent 不能调用 `comet bundle`、`comet publish`、`comet skill`,也不能执行候选 Skill 的脚本。 +- 如果状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,主会话必须补上下文、拆小任务、换更强模型或询问用户;不得继续组装。 +- 没有平台 subagent 能力时,内联 fallback 必须保留相同的 lane、claim 和 finding 结构。 diff --git a/.agents/skills/comet-any/reference/bundle-authoring.md b/.agents/skills/comet-any/reference/bundle-authoring.md new file mode 100644 index 0000000..c327f1e --- /dev/null +++ b/.agents/skills/comet-any/reference/bundle-authoring.md @@ -0,0 +1,166 @@ +# Bundle Authoring 参考 + +`comet creator` 是 `/comet-any` 的普通 CLI 表面,背后复用 `comet bundle` 的确定性后端。普通用户不需要记忆 Bundle 子命令;Skill Creator 负责把用户确认的 Workflow Contract 写成 plan,再调用 CLI 维护状态。 + +## Workflow Contract 输入 + +新的 `plan.json` 使用 `workflow` 作为主输入: + +```json +{ + "goal": "基于 /comet-classic 的五阶段定制,要求组件库和白盒审查。", + "skillCreatorIntent": "customize-comet", + "workflow": { + "kind": "comet-five-phase-overlay", + "name": "team-comet", + "goal": "要求组件库和白盒审查。", + "nodes": { + "execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "reason": "Use project component library during direct implementation." + } + ] + }, + "subagent-execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "scope": "handoff" + } + ] + }, + "review": { + "requiredSkillCalls": [ + { + "skill": "whitebox-code-standard", + "scope": "review" + } + ] + } + } + }, + "engineMode": "deterministic", + "runnerMode": "standalone" +} +``` + +字段约定: + +- `workflow.kind`: `comet-five-phase-overlay` 或 `workflow-kernel`。 +- `workflow.nodes`: 按 Node id 覆盖或增强 Workflow Node。 +- `implementation`: 替换 Node implementation。只能用于允许 override 的 Node。 +- `requiredSkillCalls`: Required Skill Call,要求 Node 内必须调用 Skill。 +- `augmentations`: 增强 Node,不替换主 implementation。每个新增 binding 或 schema 都必须声明 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +- `satisfies`: producer override 满足的 Output Schema id。 +- `outputSchemas`: 高级 `workflow-kernel` 可以声明自定义 Output Schema。Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。 +- `customNodes[].responsibility`: 自定义 Node 必须声明它在 Agent workflow 中承担的职责。 +- `customNodes[].requiredSkillCalls`: 自定义 Node 可直接声明必须调用的 Skill,生成脚本会按 evidence 阻断。 + +## 内置 Comet Nodes + +`comet-five-phase-overlay` 内置这些 Workflow Node: + +- `comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`;没有 active change 或多个 active changes 时必须阻塞并请用户选择。 +- 不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态;overlay 脚本只能读取并推进 `.comet.yaml`,其他文件只能作为 draft、eval 或 publish evidence。 +- `open`: control,保留 Comet intake 和 `.comet.yaml` 初始化。 +- `design`: producer,可 override,但必须满足 `comet.design.v1`。 +- `plan`: producer,可 override,但必须满足 `comet.plan.v1`。 +- `execute`: control,可 require / augment,例如要求 `elementui`。 +- `subagent-execute`: Handoff,可要求子代理使用 `elementui` 并回传 evidence。 +- `review`: Guardrail,可要求 `whitebox-code-standard`。 +- `verify`: control,保留验证和分支收尾。 +- `archive`: control,保留 OpenSpec archive 和 delta sync。 + +自定义 `workflow-kernel` Node 示例: + +```json +{ + "id": "delegate-notes", + "label": "Delegate Notes", + "kind": "handoff", + "responsibility": "Delegate release note drafting and require returned evidence.", + "implementation": { + "skill": "handoff-coordinator", + "scope": "handoff" + }, + "requiredSkillCalls": [ + { + "skill": "release-notes", + "scope": "handoff" + } + ], + "operations": ["require", "augment"], + "outputSchemas": ["release.notes.v1"], + "guardrails": [ + { + "id": "handoff-returned", + "label": "Handoff returned evidence", + "validation": "semantic" + } + ] +} +``` + +## 生成物 + +Factory 必须生成: + +- entry `SKILL.md` +- 每个 Workflow Node 对应的 internal Skill +- `reference/workflow-protocol.json` +- `reference/resolved-skills.json` +- `reference/decision-points.md` +- `reference/recovery.md` +- `reference/authoring-lanes.json` +- `reference/skill-review.md` +- `scripts/workflow-state.mjs` +- `scripts/workflow-guard.mjs` +- `scripts/workflow-handoff.mjs` +- `scripts/comet-plan.mjs` +- `scripts/comet-check.mjs` +- `scripts/comet-hook-guard.mjs` +- `comet/eval.yaml` + +三个 `workflow-*.mjs` 按 workflow 契约创作;三个 `comet-*.mjs` 由 factory 确定性生成(plan 别名、必需文件检查、hook guard),同样读取 `workflow-protocol.json`。 + +`workflow-protocol.json` 是 runtime、eval、review、publish readiness 的共同事实源。 + +## CLI 生命周期 + +常用 Creator 命令: + +```bash +comet creator guide --project . --json +comet creator candidates --json +comet creator propose --file --json +comet creator init --file --confirmed-proposal --json +comet creator resolve --candidate --source --json +comet creator generate --json +comet bundle compile --platform --json +comet eval /comet/eval.yaml --quick --html +comet eval /comet/eval.yaml --full --html +comet publish review --platform --json +comet publish approve --reviewer --json +comet publish run --platform --json +comet publish distribute --platform --scope project --preview --json +``` + +`` 是 compile 后生成 Skill 的目录。eval 结果必须登记为当前 draft hash 的 eval evidence;readiness 只读取该 hash 对应的证据,旧 hash 证据只能保留作审计,不能推进 ready。 + +## Readiness + +readiness 必须检查: + +- resolved Skills 无 missing / ambiguous。 +- proposal 已确认。 +- `workflow-protocol.json` 可加载。 +- control Node 没有普通 override。 +- producer override 满足 Output Schema。 +- 每个新增 binding 或 schema 都有 `guarded`、`handoff-guarded`、`evidence-only` 或 `advisory` enforcement。 +- 当前 draft hash 的 eval evidence 已存在且未过期。 +- 人工 review 已批准当前 hash。 +- capability gap 和 executable disclosure 已展示。 + +非 JSON 输出也必须向用户展示 `Readiness:`、`Blockers:`、`Warnings:`、`Evidence:`。 diff --git a/.agents/skills/comet-any/reference/eval-provider.md b/.agents/skills/comet-any/reference/eval-provider.md new file mode 100644 index 0000000..c954f05 --- /dev/null +++ b/.agents/skills/comet-any/reference/eval-provider.md @@ -0,0 +1,66 @@ +# Eval Evidence 参考 + +## Eval 选择 + +在任何验证动作前,必须展示: + +- `quick` 的预计运行次数、覆盖组件和 token 消耗。 +- `full` 的预计运行次数、覆盖组件和 token 消耗。 +- `skip / quick / full eval` 三个选择。 + +skip、eval 失败或证据 hash 过期时不得进入 ready;也不得 publish 或 distribute。 + +普通用户的验证路径保持单一:日常评估统一走 `comet eval`。`/comet-any` 可以在内部登记 eval evidence,但不要把内部登记步骤包装成面向普通用户的替代命令。对普通用户,解释时优先使用“验证”,不要把 `Publish readiness:` 当成第一层概念。 + +## 结果记录 + +eval evidence 必须输出结构化 JSON,至少包含: + +- 当前 draft hash。 +- 覆盖的 entry Skill 和 internal Node Skill。 +- `workflow-protocol.json` hash。 +- quick 或 full 的选择、token 消耗和结果摘要。 +- Bundle 编译、安全检查和 capability evidence。 + +只有当前 draft hash 的 eval evidence 可以推进状态。旧 hash 证据可以保留在磁盘用于审计,但不能让当前 draft 进入 ready。 + +## 人工评审 + +eval 通过后仍必须人工批准。评审摘要至少包含: + +先运行 `comet publish review --platform --json`,再基于其输出展示: + +- Bundle 名称、版本、hash。 +- 多个 entry 与 internal Skill 列表。 +- `planHash`、`preferenceHash` 与 `reference/resolved-skills.json` 真实 Skill 证据,包括项目级偏好模式、required Skill、`sourceSummaries` 与“整理后的工作方式”摘要。 +- 推荐调用顺序与 `preferenceIndex`。 +- 偏离偏好顺序的项和原因。 +- `.comet/skill-preferences.yaml` 是否在 Factory 初始化后发生漂移;`advisory` 模式给出 warning,`strict` 模式阻塞。 +- 稳定组合 Skill Bundle 的 required capability set(必需能力集合)`skills/scripts/rules/hooks/references` 是否声明完整,且 `scripts/rules/hooks` 是否继续作为 required control plane。 +- 是否生成 `comet/skill.yaml`、`comet/guardrails.yaml`、`comet/checks.yaml` 与 `comet/eval.yaml`。 +- `hooks/*.yaml` 是否仅被当作 portable hook descriptor,等待 `comet publish distribute` 编译到目标平台。 +- 能力缺口和可执行披露。 +- eval 选择、token 消耗和结果摘要。 +- `Validate this Skill` 与下一步提示,让用户知道 readiness 为什么可发布或被阻塞。 + +readiness blockers 会阻止 publish。只要存在当前 draft hash 的 eval evidence 缺失、人工 approval 缺失、required capability gap 或 executable disclosure 未确认,就必须停在评审阶段,不能继续发布。 + +只有用户显式批准后,才能运行 `comet publish approve` 并发布。 + +## 分发预览 + +执行真实分发前,必须先跑 preview: + +```bash +comet publish distribute --platform --scope project --preview --json +``` + +preview 是强制检查,不是可选附加项。它应向用户展示: + +- `Install preview` +- planned files +- unsupported capability +- executable disclosures +- `No files were written` + +只有当用户确认 preview 结果后,才可以移除 `--preview` 执行真实分发。 diff --git a/.agents/skills/comet-any/reference/subagents/pause-points-author.md b/.agents/skills/comet-any/reference/subagents/pause-points-author.md new file mode 100644 index 0000000..f4fb306 --- /dev/null +++ b/.agents/skills/comet-any/reference/subagents/pause-points-author.md @@ -0,0 +1,81 @@ +# 停顿点作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +设计用户在 Skill 中真正必须选择的位置,以及跨设备断点恢复方式。必须先区分四类情况:用户决策、自动处理、停止条件和手动衔接。只有存在两个或以上会改变范围、行为、风险接受或不可逆结果的合法选项时,才创建用户停顿点;单一安全动作直接执行,缺少依赖或状态损坏只报告停止条件,手动衔接只交还控制权。真正的用户决策不能被默认推荐、历史偏好或自动推进绕过。 + +必须覆盖: + +- `reference/decision-points.md` +- `reference/recovery.md` + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- Skill Creator 方案确认页中的 `confirm-generate`、`revise-proposal`、`cancel`:这是会改变生成结果的用户决策。 +- eval 工作量选择 `skip / quick / full eval` 和安装前人工批准:只有确实存在多个合法选项时才是用户决策。 +- 当前 draft hash 的 eval evidence 缺失或过期、unresolved、ambiguous、capability gap、executable disclosure:逐项判断是可自动修复、无路可走的停止条件,还是确有多个恢复选项的用户决策;不得统一写成停顿点。 +- runner 恢复状态和跨设备恢复入口:复用仍有效的持久化选择,不得在恢复时重复询问。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、 +workflow protocol 和已有草稿。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "设计 的用户停顿点和恢复" +model: <必须显式指定 model> +prompt: + 你是停顿点作者 subagent。 + 先读取本 brief、通用输入路径、workflow protocol 路径、Skill 草稿路径和报告文件路径。 + 开始前先把候选节点分类为用户决策、自动处理、停止条件或手动衔接。如果分类所需事实缺失,先返回 NEEDS_CONTEXT。 + 不要猜测缺失选择,也不要把自动修复、guard 失败、能力缺口、单一合法动作或手动衔接伪装成用户停顿点。 + 只产出 decision-points 和 recovery 草稿,不写 Bundle state,不执行候选脚本。 + 把完整停顿点草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回草稿时先列分类表,再说明真正的用户停顿点: + +- 每个候选节点属于用户决策、自动处理、停止条件还是手动衔接,以及判定依据。 +- 每个停顿点的触发条件。 +- 用户可选择项。 +- 每个选择会进入哪个阶段。 +- 停顿点证据写入哪里。 +- 自动处理如何直接推进;停止条件如何报告恢复条件而不发明选项。 +- 恢复时如何显示当前阶段、阻塞原因、建议下一步和真实可选项,并复用已持久化选择。 + +停顿点必须适配当前 workflow protocol,不得只列 Comet 原始停顿点。 + +## 自检 + +返回前逐项检查: + +- 每个用户停顿点都有触发条件、选项、下一阶段和证据位置。 +- 每个停顿点至少有两个当前可执行的合法选项;相邻且可同时回答的选择已合并,单一合法值不会制造停顿。 +- guard 失败、确定性重试、状态对账、能力缺口和 `NEXT: manual` 已按实际情况归入自动处理、停止条件或手动衔接,而不是默认询问用户。 +- 默认推荐、历史偏好和自动推进都不能绕过必须停顿点。 +- 恢复摘要能显示当前阶段、阻塞原因、建议下一步和真实可选项,且不会重复询问仍有效的选择。 +- 跨设备恢复不依赖当前会话记忆。 +- 停顿点适配当前组合 Skill,不只是照列 Comet 原始停顿点。 + +## 必须返回的 claim + +- `pause:decision-points` +- `pause:recovery` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、未解决疑虑和恢复风险。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.agents/skills/comet-any/reference/subagents/reference-author.md b/.agents/skills/comet-any/reference/subagents/reference-author.md new file mode 100644 index 0000000..434b986 --- /dev/null +++ b/.agents/skills/comet-any/reference/subagents/reference-author.md @@ -0,0 +1,81 @@ +# reference 作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写和整理候选 Skill 的 reference 层,让审计、恢复、评审和后续维护有真实依据。 +reference 层承载证据,不把内部审计内容塞进用户可见 `SKILL.md`。 + +必须覆盖: + +- `reference/workflow-protocol.json` +- `reference/resolved-skills.json` +- `reference/composition-report.md` +- `reference/authoring-lanes.json` + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- resolved Skill 的名称、来源、description、hash、reference、rules、scripts、hooks。 +- `sourceSummaries`,必须来自真实 `SKILL.md` 正文和直接 reference。 +- 项目级偏好、缺失/歧义候选处理、偏离原因和可执行披露。 +- `/comet-classic` 定制时保留的五阶段语义。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要读取主会话历史,也不要把来源 Skill 原文整段搬进 +reference。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "整理 的 reference 证据" +model: <必须显式指定 model> +prompt: + 你是 reference 作者 subagent。 + 先读取本 brief、通用输入路径、resolved skills 路径、workflow protocol 路径和报告文件路径。 + 开始前先提出问题:如果来源证据、hash、偏好决策或可执行披露不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失来源。 + 只产出 reference 草稿和 claims,不写 Bundle state,不执行候选脚本。 + 把完整 reference 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回 reference 草稿,说明: + +- workflow protocol 中每个阶段的目标、守卫、下一阶段条件和恢复点。 +- resolved Skill 证据如何支持组合流程。 +- 组合与用户偏好哪里一致、哪里偏离、为什么偏离。 +- 哪些内容属于用户可见流程,哪些内容只保留在 reference 审计层。 + +不要把 reference 写成原 Skill 的复制粘贴;必须提炼为组合 Skill 可用的证据和协议。 + +## 自检 + +返回前逐项检查: + +- 每个 source summary 都能追溯到真实 `SKILL.md` 或直接 reference。 +- workflow protocol 能解释阶段目标、守卫、自动推进和恢复。 +- 偏好、缺失、歧义、偏离和可执行披露都有记录。 +- 用户可见流程和内部审计内容边界清楚。 +- 没有把原 Skill 全文复制进 reference。 + +## 必须返回的 claim + +- `reference:workflow-protocol` +- `reference:resolved-skills` +- `reference:composition-report` +- `reference:authoring-lanes` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、证据缺口和疑虑。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么来源、 +尝试过什么、需要主会话如何处理。 diff --git a/.agents/skills/comet-any/reference/subagents/script-author.md b/.agents/skills/comet-any/reference/subagents/script-author.md new file mode 100644 index 0000000..2fbe26a --- /dev/null +++ b/.agents/skills/comet-any/reference/subagents/script-author.md @@ -0,0 +1,84 @@ +# 脚本作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +设计候选 Skill 的脚本契约,而不是复制 Comet Classic 的脚本。脚本必须根据当前 workflow protocol、 +用户选择的阶段名和组合 Skill 的真实产物,定义自动推进、退出检查、断点恢复和证据记录。 + +必须覆盖: + +- `scripts/workflow-state.mjs` +- `scripts/workflow-guard.mjs` +- `scripts/workflow-handoff.mjs` + +factory 还会从同一份 `workflow-protocol.json` 确定性生成 `scripts/comet-plan.mjs`、`scripts/comet-check.mjs`、`scripts/comet-hook-guard.mjs`。不要重复设计这三个脚本;你的契约只针对 skill-core 作者与 entry 作者引用的 `workflow-*.mjs`。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- `reference/workflow-protocol.json` +- `plan.json` 的 `workflow.kind`、`workflow.nodes`、`engineMode`、`runnerMode`,以及规范化后的 `workflow-protocol.json` +- `reference/resolved-skills.json` +- `/comet-classic` 定制时的 `.comet.yaml` 受保护语义;`comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`,不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要读取主会话历史,也不要要求用户重新解释已经写入 +artifact 的内容。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的脚本契约" +model: <必须显式指定 model> +prompt: + 你是脚本作者 subagent。 + 先读取本 brief、通用输入路径、workflow protocol 路径、resolved skills 路径和报告文件路径。 + 开始前先提出问题:如果脚本边界、Node 完成条件、状态写入或恢复语义不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失协议。 + 不要调用 comet bundle、comet publish、comet skill,也不要执行候选 Skill 的脚本。 + 把完整脚本契约写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回脚本契约草稿,说明每个脚本: + +- 读取哪些状态。 +- 写入哪些状态和 evidence。 +- 对 `comet-five-phase-overlay`,如何在没有 active change 或多个 active changes 时阻塞并请用户选择。 +- 如何判断当前阶段目标是否完成。 +- 如何输出 `NEXT:`、`SKILL:` 和阻塞原因。 +- 如何在阶段未完成时继续停留,而不是强制退出。 +- 如何支持跨设备断点恢复。 + +退出检查必须来自当前 workflow protocol 和组合 Skill 的目标,不得照搬 Comet Classic 脚本。 + +## 自检 + +返回前逐项检查: + +- 每个脚本都有输入、输出、状态读写、evidence 写入和失败行为。 +- Node 完成条件来自 workflow protocol,而不是来自固定阶段名。 +- `NEXT:` 和 `SKILL:` 的输出条件可被 Skill 核心作者直接引用。 +- 跨设备恢复不依赖当前会话记忆。 +- 没有执行候选脚本,也没有写入 Bundle state。 + +## 必须返回的 claim + +- `script:workflow-state` +- `script:workflow-guard` +- `script:workflow-handoff` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、测试/校验说明和疑虑。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.agents/skills/comet-any/reference/subagents/skill-core-author.md b/.agents/skills/comet-any/reference/subagents/skill-core-author.md new file mode 100644 index 0000000..94cd1b3 --- /dev/null +++ b/.agents/skills/comet-any/reference/subagents/skill-core-author.md @@ -0,0 +1,129 @@ +# Skill 核心作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写 internal Node Skill 的用户可见核心内容。entry Skill 由 workflow entry 作者单独负责。目标是产出 Comet-like 的多 Node workflow, +不得把来源 Skill 简单串联,也不复制粘贴原 Skill 全文。 + +必须覆盖: + +- 每个 internal Node Skill +- `comet/skill.yaml` 中的阶段调用语义 +- 每个 `node-skill:` claim + +## 创作区(你写什么) + +生成器把每个 Node SKILL.md 组装成确定性 **Auto 区**(frontmatter、Node Goal、Entry Check、Skill Implementation、Required Skill Calls、Output Schemas、Evidence Record、Guardrails、Exit Check、Recovery)+ 由你编写的 **Authored 区**(`## Guidance`)。**你只写 Guidance 正文,不写整个文件**。主会话通过 `comet creator authoring-record --lane skill-core --file ` 记录你的产出;artifact `..//SKILL.md` 的 `content` 即 Guidance 正文。 + +质量标尺:真实的 Comet 阶段 skill(如 `comet-build/SKILL.md`);完整 substance 节点 Guidance 范例见 `reference/authored-zone-example.md`。写决策内容,不写套话。在 Guidance 内用 `###` 子标题(嵌套在 `## Guidance` 之下): + +- `### Prerequisites` — 本 Node 开始前必须成立的前提。 +- `### Steps` — 有序、领域相关的步骤;按名引用绑定的 Skill 并说明何时调用(不要复制它的正文)。 +- `### Completion reasoning` — 本 Node 真正完成的判定(超出机械的 Exit Check),以及其中的判断取舍。 +- `### Red flags` — 看似进展、实则不然的失败模式。 + +下面是"Research" substance 节点的具体摘录,展示预期深度。注意它是**决策内容**(何时停止、什么算充分、什么该拒绝),不是重述路由表或 output schema。 + +```markdown +### Prerequisites + +- entry 的 Decision Core 已确认研究主题与范围。 +- `research-skill` 在项目 Skill 池中可解析;若缺失,先停下询问用户,不要临时替代。 + +### Steps + +1. 加载 `research-skill` 并按其发现方法处理已确认主题。当项目 Skill 定义了特定来源顺序时,不要用通用网络搜索替代。 +2. 按优先级收集来源;为每个来源记录来源、日期和你要复用的论点。不通过项目可信度门槛的来源应直接剔除,而不是标注为"偏弱"。 +3. 把发现提炼到 `notes/*.md` 笔记文件——每条独立论点一份,含逐字引用与来源指针。综合写在 writer 节点,不在这里。 +4. 记录 `research.notes.v1` 的 `summary` evidence:一段提炼 + 产出的笔记数量。 + +### Completion reasoning + +本节点完成当且仅当两条同时成立:(a) 已记录 `summary` evidence;(b) 至少一个 artifact 匹配 `notes/*.md`。不要仅仅因为步骤清单走完就退出——如果相对主题范围笔记仍偏稀疏,应继续研究而非宣布完成。Exit Check 脚本会机械地强制 artifact + evidence 要求;你的职责是判断研究是否真正充分。 + +### Red flags + +- 记录了 `summary` 却没有产出任何 `notes/*.md` 就退出(guardrail 会阻塞——不要试图绕过)。 +- 把来源原文复制进笔记却不加引用标记或来源指针。 +- 某来源仍"待核实"就推进到 Write 节点——核实属于本节点。 +- 对需要多视角的主题,仅凭单一来源就认为充分。 +``` + +节点模式(来自 protocol): + +- **substance** 节点(workflow-kernel 默认):必须有富 Guidance。缺失时该节点渲染为 `AUTHORING PENDING`,Bundle 不得 ready。 +- **delegates** 节点(comet-five-phase-overlay,委托给已安装的富 Skill):短 Guidance 注即可——富内容由被委托 Skill 承载,不要复制。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- 用户确认的阶段名和可输入名字项。 +- `reference/workflow-protocol.json` 的阶段目标、`requiredSkillCalls` 与自动推进条件。 +- `reference/resolved-skills.json` 的真实 Skill 摘要。 +- 脚本作者返回的 `NEXT:`、`SKILL:`、guard 和 recovery 契约。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、 +脚本契约和 reference 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的 Skill 核心内容" +model: <必须显式指定 model> +prompt: + 你是 Skill 核心作者 subagent。 + 先读取本 brief、通用输入路径、脚本契约路径、reference 证据路径和报告文件路径。 + 开始前先提出问题:如果阶段名、必须调用的 Skill、自动推进或用户停顿点不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失流程。 + 只写 internal Node Skill 草稿,不写 entry Skill,不写 Bundle state,不执行候选脚本。 + 把完整 Skill 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回 internal Node Skill 草稿,必须体现: + +- internal Node Skill 负责单 Node 目标、必须调用的 Skill、Node 完成证据和脚本守卫。 +- 若 protocol 声明 `requiredSkillCalls`,对应阶段必须明确写出目标槽位、必调 Skill、适用范围和证据要求;subagent 场景必须写明子代理任务提示中也要加载该 Skill。 +- 阶段未达成目标时继续工作,不因为流程清单走完就退出。 +- 自动推进必须来自脚本输出的 `NEXT:` 和 `SKILL:`,而不是让 Agent 猜下一步。 +- 嵌套 Skill 调用只写 Skill 名字,不写 provider 前缀。 +- 对 `/comet-classic` 定制,保留 `open / design / build / verify / archive` 与 `.comet.yaml` 语义。 +- 对任意 Skill 组合,整理为 Comet-like 多 Node workflow。 + +禁止: + +- 复制粘贴原 Skill 全文。 +- 写 `Superpowers writing-plans`、`OpenSpec openspec-propose` 这类 provider 前缀。 +- 在中文 Skill 中混入英文流程句。 +- 把审计报告、source hash、内部 metadata 泄漏到用户可见 `SKILL.md`。 + +## 自检 + +返回前逐项检查: + +- 每个阶段都说明必须调用的 Skill、完成证据、脚本守卫和恢复入口。 +- 每个 `requiredSkillCalls` 都能在对应Node Skill 中找到明确加载指令和证据要求。 +- 自动推进引用脚本输出的 `NEXT:` 和 `SKILL:`。 +- Skill 调用只写 Skill 名字,不写 provider 前缀。 +- 中文用户可见文案没有混入英文流程句。 +- 没有复制粘贴原 Skill 全文。 + +## 必须返回的 claim + +- 每个 internal Node Skill 的 `node-skill:` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、未解决疑虑和建议返工点。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.agents/skills/comet-any/reference/subagents/skill-reviewer.md b/.agents/skills/comet-any/reference/subagents/skill-reviewer.md new file mode 100644 index 0000000..56a7245 --- /dev/null +++ b/.agents/skills/comet-any/reference/subagents/skill-reviewer.md @@ -0,0 +1,126 @@ +# Skill 审查 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +审查其他 subagent 的 artifacts 和 claims,判断候选 Skill 是否已经像 Comet 一样可用。 +审查必须给出明确结论:`Review passed` 或 blocking findings。 + +必须覆盖: + +- `reference/skill-review.md` +- `reference/authoring-lanes.json` + +## 输入 + +读取主会话提供的通用输入,以及其他五个作者角色返回的全部 artifacts、claims 和 findings。 + +使用文件交接:主会话提供路径,不粘贴大段全文。读取总览、通用输入、五个作者报告文件、artifact 路径和 +claims。不得读取主会话历史来替代 artifact 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "审查 的 Comet-like Skill 产物" +model: <必须显式指定 model> +prompt: + 你是 Skill 审查 subagent。 + 先读取本 brief、通用输入路径、五个作者报告路径、artifact 路径、claim 清单和报告文件路径。 + 审查不信任作者报告;作者报告只是 claim,必须用 artifact 和 claim 交叉验证。 + 不要告诉审查者不要标记某问题,也不要预设某问题只能是 Minor。 + 审查是只读任务,不得修改工作树、索引、HEAD 或分支状态。 + 把完整审查写入报告文件路径,并返回两个 verdict。 +``` + +## 审查方法 + +审查不信任作者报告。先看 artifact 和 claim,再判断作者声称是否成立。不得用“作者说这是刻意设计” +来降低问题严重性。 + +审查必须给两个 verdict: + +- Skill 契约符合度:是否满足用户确认的目标、workflow protocol、`requiredSkillCalls`、claim、阶段推进、脚本守卫、停顿点和恢复要求。 +- 可用性质量:是否像 Comet 一样好用,是否清晰、可恢复、可审计、不会过度暴露内部 metadata。 + +证据必须引用 artifact 路径和 claim。不能只写“看起来可以”。 + +## 阻塞条件 + +出现以下任一情况必须给出 blocking findings: + +- 缺少 `reference/skill-review.md`。 +- 缺少 `reference/authoring-lanes.json`。 +- 缺少 workflow entry 作者、脚本作者、reference 作者、Skill 核心作者或停顿点作者的关键 claim。 +- entry Skill 把阶段路线写成多个 `**立即执行:**` Node Skill,而不是通过状态脚本只路由当前阶段。 +- 缺少 `workflow-state.mjs`、`workflow-guard.mjs` 或 `workflow-handoff.mjs` 契约。 +- 包内缺少六个生成脚本中的任何一个(`workflow-state`、`workflow-guard`、`workflow-handoff`、`comet-plan`、`comet-check`、`comet-hook-guard`)。 +- workflow entry 缺失,或 Skill 核心没有 internal Node Skill。 +- 阶段推进没有通过脚本输出 `NEXT:` 和 `SKILL:` 表达。 +- workflow protocol 声明的 `requiredSkillCalls` 没有在对应Node Skill 中明确要求加载,或 subagent 槽位没有要求子代理任务提示加载该 Skill。 +- 用户停顿点缺失,或停顿点可被默认值绕过。 +- 把确定性修复、guard 失败、状态对账、能力缺口、单一合法动作或 `NEXT: manual` 默认写成用户停顿点;或把可同时回答的相邻选择拆成连续确认。 +- entry Skill 的 frontmatter description 没有说明它是托管 workflow 的入口/恢复路由,或 internal Node Skill 的 description 允许普通任务直接触发而未限定为显式调用或 entry/runtime 路由。 +- 中文 Skill 混入英文流程句。 +- 嵌套 Skill 调用使用 provider 前缀。 +- 用户可见 `SKILL.md` 泄漏生成审计章节、source hash 或内部 metadata。 +- `/comet-classic` 定制替换或删除了 `open / design / build / verify / archive`、`.comet.yaml`、decision point、verify-result-transition 或 archive-delta-sync。 +- 任意 Skill 组合缺少自动推进、脚本守卫、用户停顿点、恢复或当前 draft hash 的 eval evidence。 + +## 严重级别 + +- Critical:会让生成 Skill 不可用、不可恢复、不可审计,或破坏 `/comet-classic` 受保护语义。 +- Important:会让阶段流程、脚本守卫、停顿点、Skill 调用或证据链不可信;必须修复后才能 ready。 +- Minor:不阻塞 ready 的清晰度、命名或维护性改进。 + +## 输出要求 + +返回: + +- `reference/skill-review.md` +- `reference/authoring-lanes.json` +- `review:skill-review` +- 最终 `Review passed` 或 blocking findings。 + +输出必须包含: + +- 两个 verdict:Skill 契约符合度、可用性质量。 +- Strengths:具体说明做得好的 artifact。 +- Issues:按 Critical、Important、Minor 分组。 +- 每个 finding 的 artifact 路径、claim、问题、影响和建议修复方式。 +- `Review passed` 或 blocking findings。 + +如果存在 Critical 或 Important,不得给出 `Review passed`。如果作者状态是 `BLOCKED` 或 `NEEDS_CONTEXT`, +必须返回 blocking findings,主会话必须补上下文、拆小任务、换更强模型或询问用户;不得继续组装。 + +## 多视角审查(按 depth) + +skill-review lane 是 DAG 的 barrier,在所有创作 lane 之后运行。其裁决用 `comet creator authoring-record --lane skill-review --file ` 记录,成为真实的 `skill-review.md` / `authoring-lanes.json` 证据——绝不是硬编码的批准。 + +- `depth: full`:派发 N=3 个独立审查者,每人只持一个 lens(`contract-fit`、`usability`、`evidence-trace`、`self-consistency`),互不见彼此结论。跨审查者的 findings 去重后,仅多数确认才成立。重复直到连续 `dryThreshold` 轮无新的 Critical/Important(上限 `maxRounds`)。`self-consistency` 必须交叉校验:引用的命令可解析、声明产物 == 实际产物、无幽灵命令引用(未注册的命令名)残留。 +- `depth: quick`:单审查者、单轮。 +- 仅当无 Critical/Important 残留时 `passed` 为 `true`。Minor 记录但不阻塞。 + +记录的 review 对象必须符合此形状(由 `authoring-record` 校验): + +```json +{ + "lane": "skill-review", + "status": "DONE", + "review": { + "passed": true, + "evidenceSource": "llm-multivote", + "voters": 3, + "lenses": ["contract-fit", "usability", "evidence-trace", "self-consistency"], + "rounds": 2, + "findings": [ + { "severity": "minor", "path": "SKILL.md", "problem": "措辞小问题。", "fix": "改写。" } + ], + "reviewedAt": "2026-06-28T00:00:00.000Z" + } +} +``` + +`passed: false` 的审查是 readiness blocker(不得 publish);缺失审查是 warning(包仍带诚实的 `deterministic-check-only` 占位,绝不伪造批准)。 diff --git a/.agents/skills/comet-any/reference/subagents/workflow-entry-author.md b/.agents/skills/comet-any/reference/subagents/workflow-entry-author.md new file mode 100644 index 0000000..9cab911 --- /dev/null +++ b/.agents/skills/comet-any/reference/subagents/workflow-entry-author.md @@ -0,0 +1,99 @@ +# Workflow Entry 作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写生成 Skill 的 entry `SKILL.md`。entry 只负责入口、恢复、主路由说明和用户停顿点;不得把阶段路线写成会立即触发多个 Skill 的执行清单。 + +必须覆盖: + +- entry Skill +- workflow-state / workflow-guard / workflow-handoff 的入口说明 +- `workflow-entry` claim + +## 创作区(你写什么) + +生成器把 entry SKILL.md 组装成确定性 **Auto 区**(frontmatter、Workflow Nodes 路由表、Skill Bindings、Guardrails And Evidence、Runtime And Recovery)+ 由你编写的 **Authored 区**(`## Decision Core`)。**你只写 Decision Core 正文,不写整个文件**。主会话通过 `comet creator authoring-record --lane workflow-entry --file ` 记录;artifact `SKILL.md` 的 `content` 即 Decision Core 正文。 + +质量标尺:`comet/SKILL.md` 的 Decision Core(完整 entry Decision Core 范例见 `reference/authored-zone-example.md`)。写 agent 可读的决策规则——机械路由已由 Auto 区的 `workflow-state.mjs next` 处理,所以聚焦判断: + +- **语义化当前节点检测** — 如何判断用户在哪个 Node,而非只跑脚本。建模 comet 的 Step 0(从用户消息检测意图,检查 Node 顺序,处理"属于前序/后序 Node"的冲突)+ Step 1(读状态,文件优先于过期状态)。 +- **Resume 与 drift 规则** — 上下文恢复时怎么办(从头重新检测,永远不信任对话历史),状态说 DONE 但 artifact 缺失时怎么办,用户在 Node 中途换话题时怎么办。 +- **决策分类与决策点** — 先区分用户决策、自动处理、停止条件和手动衔接,再只为真正需要用户选择的情况建表。清晰的首次调用、可确定修复的 guard 失败、单一合法下一步和 `NEXT: manual` 都不得制造确认点。 +- **Red flags** — "agent 想法 → 实际风险"模式,抓自欺(如"用户提到了主题所以研究已确认" → 提到 ≠ 确认)。 + +没有这四个子节的 Decision Core 是 stub,不是 Decision Core。entry 是每次调用最先读取的文件——它决定了 Skill 感觉"智能"还是"机械"。 + +Auto 区的 Node 路由表仅供参考——不要复制成执行清单,不要发出多个立即 Skill 加载。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- 用户确认的目标、语言和阶段名。 +- `reference/workflow-protocol.json` 的阶段顺序、插槽、`requiredSkillCalls` 和恢复路径。 +- 脚本作者返回的 `status`、`init`、`next`、`NEXT:`、`SKILL:` 和 guard 契约。 +- `/comet-classic` 定制场景下必须保留的 open / design / build / verify / archive 边界。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、脚本契约和 reference 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的 workflow entry" +model: <必须显式指定 model> +prompt: + 你是 workflow entry 作者 subagent。 + 先读取本 brief、通用输入路径、脚本契约路径、workflow protocol 路径和报告文件路径。 + 开始前先分类用户决策、自动处理、停止条件和手动衔接;如果启动路由、恢复路径、当前阶段判定或真正的用户选择不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失流程。 + 只写 entry SKILL.md 草稿,不写 internal Node Skill,不写 Bundle state,不执行候选脚本。 + Decision Core 必须包含四个子节:### 自动节点检测(Step 0 意图检测 + Step 1 状态读取 + Resume 规则)、### 决策分类与决策点(只列真正的用户选择)、### Red Flags(agent 想法 → 实际风险表格)。不得把 guard 失败、确定性修复、单一合法动作或手动衔接列为用户决策。没有这些子节的 Decision Core 是 stub。 + 把完整 entry 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +entry 草稿必须体现: + +- 进入 Skill 后先读取 workflow 状态,不直接加载Node Skill。 +- 未启动时先初始化状态,再查询 `next`。 +- 只有脚本输出 `NEXT: auto` 和 `SKILL: ` 后,才加载这一个 Node Skill。 +- 阶段路线只能作为参考表,不能使用“立即执行”或“必须加载”这类执行指令。 +- 对 `/comet-classic` 定制,entry 必须列出必调槽位 Skill,但只能作为阶段内义务说明,不能变成 entry 立即执行清单。 +- 用户决策、自动处理、停止条件、手动衔接、恢复路径和参考文件清楚可见;只有至少两个真实合法选项时才停顿,相邻选择应合并。 +- 对 `/comet-classic` 定制,说明保留 open / design / build / verify / archive 主路径和阶段守卫。 + +禁止: + +- 在 entry `SKILL.md` 中写多个 `**立即执行:**` Node Skill。 +- 复制粘贴原 Skill 全文。 +- 写 provider 前缀。 +- 把审计报告、source hash、内部 metadata 泄漏到用户可见 `SKILL.md`。 + +## 自检 + +返回前逐项检查: + +- Decision Core 包含全部四个必须子节:自动节点检测、决策点、Red Flags、Error Handling 或 Resume Rules。 +- entry 只有一个主路由启动协议。 +- entry 没有立即加载Node Skill 的清单。 +- 阶段路线是参考,不是执行步骤。 +- 自动推进引用脚本输出的 `NEXT:` 和 `SKILL:`。 +- 清晰首次调用直接初始化;guard 失败先自动诊断或报告停止条件;`NEXT: manual` 只交还控制权;以上情况均不伪造用户决策。 +- 中文用户可见文案没有混入英文流程句。 + +## 必须返回的 claim + +- `workflow-entry` + +缺少该 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、claim 列表、未解决疑虑和建议返工点。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、尝试过什么、需要主会话如何处理。 diff --git a/.agents/skills/comet-native/SKILL.md b/.agents/skills/comet-native/SKILL.md new file mode 100644 index 0000000..a584acb --- /dev/null +++ b/.agents/skills/comet-native/SKILL.md @@ -0,0 +1,265 @@ +--- +name: comet-native +description: 当用户明确调用 /comet-native、要求启动或恢复 Native change,或入口路由到 Native 时使用;负责澄清需求、读取状态并推动 Shape → Build → Verify → Archive。 +--- + +# Comet Native + +Native 保存需求、完整目标规格、状态和证据。你负责理解、实现和验证;Runtime 负责状态、边界和恢复。 + +整个流程在本 Skill 内完成。不要加载阶段 Skill,也不要强制套用 Plan、TDD、Debug 或 Review 方法。 + +## 需求澄清协议 + +从 `.comet/config.yaml` 读取 `native.clarification_mode`。允许值为 `sequential` 和 `batch`;字段缺失时使用 `sequential`。该配置决定用户问题的组织方式和离开 Shape 前的确认契约,不改变 Native 的阶段、change schema、安全确认或调用方停点。 + +先识别会改变用户可见结果、但尚未定义的分支。“规范化”“直观”“标准”“预期”等词不是产品契约;只有用户原话、用户确认的答案,或明确适用于当前行为的公开契约可以关闭这类分支。 + +仓库现状、依赖默认值、相邻功能和行业惯例只能支持推荐,不能代替用户决定。“保持现有行为”只约束已经存在的结果,不自动定义新行为。 + +先判断分支是否只影响实现方式,而不改变任何用户可见结果。不能证明这一点时,按用户决定处理。即使用户要求“不要询问实现选择”,也不能把产品决定重新归类为实现选择。 + +能从仓库、工具或运行环境查明的事实由你负责调查,不交给用户回答。宿主支持并行工作时,可以并行调查相互独立的事实,但不能把并行能力作为流程前提。尚未查清的事实只阻塞依赖它的问题,不阻塞其他已经具备条件的问题。 + +只有共同定义同一个用户决定的多个细节才合并为一道策略问题。彼此独立的用户决定不能合并:Sequential 模式逐轮处理,Batch 模式为每项分别编号。不要为了增加问题数量制造歧义,也不要把实现选择放入用户问题清单。若一道问题仍未覆盖同一决定的某种合理解释,应拓宽这道问题,而不是另造一个依赖关系不清的问题。 + +### 提问载体 + +提问前先检查当前宿主的工具列表。当前工具列表提供 `AskUserQuestion` 时,Claude Code 下优先使用它展示结构化选项;其他宿主使用等价的用户输入工具。每个选项都要有简短标签和影响说明,推荐项在说明中标明,但不能自动代用户选中。 + +- Sequential 模式每轮提交一道结构化问题。选项互斥时使用单选;只有同一个用户决定本身允许同时选择多个彼此兼容的选项时,才使用多选。不能用一道多选题压缩多个独立的用户决定。 +- Batch 模式在整组问题都能满足当前工具的问题数、选项数和字段限制时,把本轮整组问题放在同一次调用中。不能把同一轮拆成多次工具调用,让后面的问题在用户回答前无法同时展示。 +- 当前宿主没有结构化提问工具,或 Batch 整轮无法在一次调用中完整表达时,整轮使用编号文本降级模式。文本必须保留相同的问题、选项、推荐和影响,然后停止并等待用户回复编号。 +- 若第一次调用失败或宿主报错,判定本会话结构化提问不可用,当轮改用文本降级模式,本会话后续不再重试。工具调用成功后直接等待用户作答,不再同时输出一套重复的文本问题。 + +### Sequential 模式 + +把当前目标视为一棵用户可见结果的决策树。进入 Shape、用户每次回答后,以及相关仓库事实发生变化后,都必须重新执行以下循环: + +1. 先调查当前分支依赖的仓库、工具和运行环境事实,不把可自行查明的事实问给用户。 +2. 从目标向下逐一遍历所有合理可达的用户可见分支,包括当前答案会引出的下游边界、失败结果和默认行为。不能在得到第一个可行解释后停止,也不能用推荐项静默关闭未确认分支。 +3. 在正式产物中维护可复核的未决事项及必要依赖摘要,不保存隐藏推理或完整内部推演。若仍有多个独立决定,选择前置条件已满足且最上游的一个;其他决定留到后续轮次。 +4. 在 brief 中只记录当前一道 `- [blocking] <问题>`。一次只问最上游的一个问题:每轮只询问这一个用户决定,不把多个独立决定塞进同一道多选题或多个并列问句。 +5. 给出“问题 / 推荐 / 影响”,用选项、正例或反例让差异可判断,然后结束本轮。推荐必须明确,但不能代用户选择。新答案引出的每一道下游或依赖问题也必须完整保留这三部分,不能只给一个候选集合后要求用户增删。 + +用户回答后,立即把确认内容写入 Decisions 和完整目标规格,移除当前阻塞项,再从目标开始重新遍历整棵决策树。目标规格尚不存在时,必须在处理该答案的同一轮创建;只更新 brief、等到最终确认或 Build 才补规格,都不算完成答案持久化。把答案当作产品约束,不当作获准采用的实现算法:一轮答案只能关闭该问题明确选择的输入→输出结果,不能因为某种实现会顺带处理其他反例,就把独立的空输入、失败或嵌入边界静默视为已决定。新答案引出的分支必须进入后续轮次;模糊、部分或未回答的内容继续保持 `[blocking]`。 + +当没有未决用户决定时,仍不能直接进入 Build。先进行一次完整性复核,主动寻找被遗漏或静默假设的用户可见分支;然后向用户给出包含目标、范围、关键决定、验收标准和明确非目标的共享理解摘要,并在 brief 中记录 `- [blocking] CONFIRM: <确认内容>`。这次复核也是可追溯性检查:摘要和验收示例中的每一项产品行为都必须能追溯到用户原话、某轮已确认答案或明确适用的公开契约;“一致”“直观”“通常如此”和仓库惯例不算确认。若某项策略第一次出现在摘要中,说明澄清循环尚未完成,必须把它重新标为用户问题,而不能借最终确认一次性决定。用户明确确认前,不修改项目实现、不进入 Build、也不调用 `next`。用户补充或否定时,更新正式产物并重新执行上述循环;明确确认后移除该阻塞项,记录确认,并使用 `--confirmed` 推进。 + +### Batch 模式 + +把当前目标视为一棵用户可见结果的决策树,并把尚未确定的用户决定按前置关系组织起来。只需要维护可复核的未决事项、依赖摘要和正式产物,不保存隐藏推理或完整内部推演。 + +每轮计算“本轮可回答问题集”:其中每个问题的前置决定都已确定,所需环境事实也已查清,并且答案不依赖本轮其他问题。这些问题组成当前 frontier;依赖仍未确定决定或调查中事实的问题留到后续轮次。 + +某个候选分支需要环境事实时,事实调查仍由你负责。宿主支持 sub-agent 或其他并行工作时,必须并行启动相互独立的事实调查;调查中的事实只暂缓依赖它的下游问题,其他 frontier 问题必须在当前轮继续提出。宿主不支持并行时,由当前执行者直接调查,不能把并行能力作为流程前提,也不能把可查明的事实转交用户。 + +对本轮可回答问题集执行以下步骤: + +1. 在 brief 的 Open questions 中使用 `- [blocking] Q1: <问题>`、`- [blocking] Q2: <问题>` 的固定格式分别保存各题;不要使用 Markdown 有序列表代替该前缀。 +2. 一次提出整组问题,并为每题给出“问题 / 推荐 / 影响”。编号应允许用户用“1 按推荐,2 选择 B”这类方式回答。 +3. 更新正式产物并提出问题后结束本轮,不进入 Build,也不调用 `next`。 + +使用以下格式: + +```text +1. 问题:…… + 推荐:…… + 影响:…… + +2. 问题:…… + 推荐:…… + 影响:…… +``` + +用户回答后,把已确认内容写入 Decisions 和完整目标规格,移除对应 `[blocking]`;没有回答或回答不明确的问题继续保持 `[blocking]`,不得按推荐项自行补全。随后根据新答案重新计算本轮可回答问题集,逐轮处理新出现的分支。 + +当本轮可回答问题集为空、相关事实已经查清且所有已识别用户决定均已处理时,执行一次完整性复核,重新检查是否仍有未处理或被静默假设的用户可见分支。向用户给出包含目标、范围、关键决定、验收标准和明确非目标的共享理解摘要,并在 brief 中使用 `- [blocking] CONFIRM: <确认内容>` 记录最终确认。用户明确确认前,不进入 Build,也不调用 `next`;用户补充或否定时,更新相应分支并继续下一轮。明确确认后,移除该阻塞项、记录确认,并使用 `--confirmed` 推进。 + +以文本“规范化”为例,一个完整问题应同时说明大小写折叠、外围标点、内部标点或撇号保留,并用反例展示不同选择的输出。 + +达成共享理解前,可以调查仓库事实、创建或恢复 Native change,并在 brief 中记录 `[blocking]`。不要进入 Build、修改项目实现或调用 `next`。 + +用户回答后,更新原有 change 的 brief 和完整目标规格,再重新检查是否还有用户决定。不要为补充答案创建第二个 change,也不要把未确认选项写成既定规格。 + +离开 Shape 时,只有本轮刚记录了用户对既有阻塞问题的确认,才给 `next` 传 `--confirmed`。Batch 模式的最终共享理解确认属于这类确认;用户最初提出需求不算。 + +若调用方要求在该 transition 后停下或切换会话,严格执行以下序列:更新正式产物 → 调用一次允许的 transition → transition 成功后不再调用工具 → 输出约定标记并结束本轮。即使 Runtime 返回 `continuation.disposition: continue`,也不能越过这个停点。 + +## 执行边界与状态快照 + +调用方指定停点时,只完成停点前允许的工作。下一会话重新调用 `/comet-native`,从磁盘读取 status、selection 和正式产物后继续,不依赖聊天记忆恢复进度。 + +调用方若要求保留某次状态变化前的 Runtime 返回快照,应在执行该状态变化之前,使用真实命令的机器可读模式,并通过重定向直接保存标准输出。快照确认完整后不得重建、刷新或覆盖;它只反映生成时的真实状态,不能根据最终结果补写。 + +## 开始或恢复 + +`/comet-native` 是 Skill 入口,不是 shell 命令。通过宿主的 Skill 机制调用;不要在 shell 中执行 `/comet-native`。 + +先运行 Native `status` 和 `show`。恢复 Verify 或 Archive 时,运行 `status --details`,读取有预算的验收页、详细 findings、`findingsTruncated` 和最新 checkpoint。 + +- findings 被截断时,先处理已返回项,再重新读取 details。 +- `acceptancePage.nextCursor` 非空时,按命令参考继续分页。 +- 随后读取 `.comet/config.yaml`,确定 `native.clarification_mode`,再读取 `comet-state.yaml`、brief、拟议规格、canonical 规格、仓库实现、项目规则和相关测试。 +- 磁盘与仓库事实优先于聊天记忆;能从环境取得的事实不要询问用户。 + +已有 active change 时,先只读确认哪个 change 对应当前目标。确认后显式运行: + +```text +comet native select +``` + +该命令建立项目级共享 selection。不要新增 `resume` 命令,也不要依赖 `status` 或 `show` 的读取副作用建立归属。 + +存在多个 active change,且 selection 不能唯一确定目标时,让用户选择。只有磁盘事实证明没有 active change 时,才把目标归纳为 lowercase kebab-case 名称并创建: + +```text +comet native new --language zh-CN +``` + +只使用配置指定的 `/comet/`,不扫描或修改其他工作流目录。 + +命令与 Runtime 定位见[命令参考](reference/commands.md),产物格式见[产物参考](reference/artifacts.md),中断与恢复见[恢复参考](reference/recovery.md)。自带 Runtime 位于 [scripts/comet-native-runtime.mjs](scripts/comet-native-runtime.mjs)。 + +项目只安装一份 Comet 工作流 Rule;支持 Hook 的平台只安装一个 `comet-hook-router.mjs`。Rule 与 Router 根据 `.comet/config.yaml` 和 `.comet/current-change.json` 确定当前 workflow,一次写入最多路由给一个 Guard。 + +当前 change 属于 Native 时,只应用 Native 的 Shape、Build、Verify、Archive 边界。不要同时运行 Native 与 Classic Guard,也不要用默认 workflow 猜测当前 change 的归属。Native 主流程不依赖任何外部 Skill。 + +## 决策协议 + +维护一份未决事项清单,按依赖顺序处理仍没有唯一答案的用户可见分支。重点检查: + +- 输出与默认行为; +- 边界条件与失败结果; +- 范围、风险和不可逆操作; +- 明确适用于当前行为的已有约束。 + +把关键名词或动作改写成可区分解释的“输入 → 输出”或“触发 → 结果”示例。一个反例能区分两种合理解释,就说明仍需用户决定。 + +文本或 token 行为通常要检查大小写、首尾与内部标点、空白、Unicode、空输入、重复项、顺序和并列结果。CLI 或 API 行为通常要检查默认值和错误结果。不要为了覆盖清单制造不存在的歧义。 + +对解析、计数、分词或边界识别,完整性复核还要用反例检查:空输入与纯空白、没有分隔符、连续或混合分隔符,以及看似分隔符但属于有效内容的字符(例如缩写中的句点、数字中的小数点或转义后的分隔符)。只有任务范围内的反例才需要询问;明确非目标应直接排除。若两个反例可以独立选择不同结果,就不能合并为一个用户决定。契约只覆盖“非空输入”时,不能把它当作空输入行为的依据。 + +只有用户给出的信息、明确非目标、已确认决定,或当前能力的明确公开契约可以消除分支。发现阻塞后,按需求澄清协议为当前模式计算并提出一个问题或本轮可回答问题集;回答前不要调用 `next` 或修改项目实现。 + +当未决事项清单为空,且仅凭 brief、完整目标规格、仓库事实和项目规则就能实现并验收,两种模式都先完成最终共享理解确认;Sequential 每轮仍只提出一个用户决定,Batch 仍按本轮可回答问题集成组提出。 + +## 推进契约 + +Shape、Build、Verify 的 transition 会返回 `next: auto | manual`,以及 `continuation.disposition: continue | await-user | blocked | done`、所需输入和下一动作。Archive 不通过 `next` 推进;成功归档才返回 `done`。 + +这些字段组成机器可读的 continuation 契约。`next: auto` 只表示当前 transition 已成功,不代表宿主会在后台执行后续工作。 + +收到 `next: auto` 且 disposition 为 `continue` 后,重新读取返回的 phase 和必要产物。没有用户决定或 Runtime 阻塞时,在本 Skill 内持续推进下一阶段,不等待用户再次触发。 + +若 disposition 为 `await-user`、`blocked` 或 `next: manual`,先根据磁盘事实和 blocking findings 处理。只有缺少的输入确实属于用户决定时才提问。 + +Sequential 模式的当前问题和最终共享理解确认,以及 Batch 模式中尚未回答的问题和最终共享理解确认,都保持为 `[blocking]`。它们是需要等待用户输入的正常停点,不能通过自动推进绕过。 + +`workspace-root-changed` 与 `workspace-inspection-unavailable` 是只读提示,不单独阻止推进或归档。未知 workspace finding、确定冲突、失效证据和 repair stop 必须处理。 + +长任务需要保留阶段内进度时,使用 `comet native checkpoint` 保存简短摘要、下一动作和真实产物引用。checkpoint 不推进 phase,也不替代 brief、规格或验证报告;不要另建 resume、handoff 或任务清单。 + +## Shape + +确认并写入:Outcome、Scope、Non-goals、Acceptance examples、Constraints and invariants、Decisions、Open questions、Verification expectations。阻塞问题在 brief 中标记为 `- [blocking]`;Batch 模式可以同时保存本轮全部可回答问题。 + +只有当 brief、完整目标规格、仓库事实和项目规则足以让后续执行者在不猜测用户可见行为的情况下实现并验收,Shape 才算完成。 + +- 更新 `brief.md`,使其能够约束实现与验收。 +- 用户明确给出的 lowercase kebab-case capability ID 必须原样用于 `specs//spec.md`。 +- 用户只给出显示名称时,在正文保留原名,并稳定派生 lowercase kebab-case capability ID。 +- 长期行为发生变化时,写归档后的完整目标规格,不写增量 patch。 +- 删除 capability 时运行 `comet native spec remove `;operation 和 canonical base hash 由 Runtime 推断并冻结。 +- 仍有未决事项时保留 `[blocking]` 并停下。 + +准备完成后运行: + +```text +comet native next --summary <摘要> [--confirmed] +``` + +仅在本轮刚记录用户对既有阻塞问题的确认时追加 `--confirmed`;Sequential 和 Batch 模式都必须先取得最终共享理解确认。Runtime 会把 approval 绑定到当时的 brief/spec contract hash;若 Build 中 contract 发生变化,先取得用户对当前 contract 的确认,再按 status 返回的命令重试。不要手工编辑 `approval` 或 `approved_contract_hash`。 + +## Build + +选择满足 brief 与拟议规格的最简单可靠方案。实现方式、是否保存计划、测试粒度、调试方法和审查强度由你根据风险决定。 + +不要为流程制造额外文档。发现需求或规格漂移时,先更新 Native 产物;出现新的用户决定时标记 `[blocking]`,按当前配置的澄清协议处理。Sequential 模式需要重新遍历决策树,Batch 模式需要重新计算问题集;两种模式都要在继续实现前取得更新后共享理解的最终确认。 + +完成后提供真实项目产物;没有代码变化时给出明确理由。然后运行: + +```text +comet native next --summary <摘要> --artifact <项目内路径> [--confirmed] +``` + +没有代码变化时按命令参考使用 `--no-code-reason`。Runtime 会返回 implementation scope 和首个 `acceptancePage`;保存 Runtime 派生的验收 ID,并按 `nextCursor` 读取全部页面。不要自行计算 ID。 + +Git 快照只包含 tracked 和未被 ignore 的 untracked 文件,submodule/gitlink 作为原子条目。非 Git 项目使用有界物理树快照。 + +- `git-selection-changed`:等待 Git 写入稳定后重试,不能授权为 partial scope。 +- `git-enumeration-limit`:先缩小或清理项目所有范围;只有 Runtime 返回可授权 scope,且用户接受未枚举尾部的具体风险时,才能使用 partial 协议。 +- `physical-selection-changed` 或 `physical-enumeration-limit`:等待文件系统稳定或缩小项目树后重试,不能授权为 partial scope。 + +Runtime 无法证明 scope 完整时会停在 Build,返回 partial scope hash 和未归属项。先补充真实 artifact 或消除未归属变化。确实只能接受 partial 时,说明具体缺口并取得用户确认,再使用同一个 hash: + +```text +--allow-partial-scope --partial-reason <理由> --confirmed +``` + +不要手改 snapshot、evidence 或猜测未枚举路径,也不要把 partial 写成 complete。 + +## Verify + +根据 Acceptance examples、完整目标规格和风险运行验证。记录实际命令、结果、跳过项、规格一致性、已知限制和结论;未运行的检查不能写成通过。 + +在 `verification.md` 的固定 acceptance evidence 块中逐项使用 Runtime 返回的 `acceptance_id`。每项只能记录项目相对 evidence refs,或记录诚实的 `skipped_reason`。用 `comet native evidence format` 把条目序列化成规范文本再粘贴,不要手工排版这段 JSON——手写几乎不可能逐字节匹配规范序列化规则。格式见产物参考。 + +需要一份可重建的文本卫生证据时,可运行内置只读检查: + +```text +comet native check +``` + +该命令只扫描当前 implementation scope/current snapshot 中有界的项目内普通文本文件,不调用 Git、shell、项目脚本、外部进程或外部 Skill。它不会修改项目文件、phase、Run 或 trajectory;结果写入内容寻址 receipt。它不替代按风险选择的项目测试。 + +写完报告后运行: + +```text +comet native next --summary <摘要> --result pass|fail --report verification.md [--receipt ] +``` + +fail 会回到 Build。修复后重新验证,并用 `--failure-category` 与 `--failed-check` 提交稳定、非敏感的失败事实。 + +同一失败第二次出现会告警;第三次且 scope 没有进展会停止。scope 发生真实变化会结束当前 repair episode。scope 未变化但有明确新假设时,可按 status 返回的 signature 使用一次 `--override-repair`;同一 signature 不得重复 override。达到停止条件后请用户决定,不要弱化检查或伪造 pass。 + +进入 Archive 后,brief、规格、implementation scope、报告或 receipt 发生变化会使证据失效。按 Runtime continuation 回到 Build,重新封印 scope 并验证;不要沿用失效的 pass。 + +## Archive + +状态进入 Archive 且 Verify 为 pass 后,先预演: + +```text +comet native archive --dry-run +``` + +检查 create/replace/remove、证据新鲜度、当前 Native root 内的 change 重叠和恢复状态。没有阻塞时,用本次预演返回的精确 hash 提交: + +```text +comet native archive --expect-preflight +``` + +调用方要求保存预演或提交 envelope 时,首次调用本身就使用机器可读模式并写入目标文件。提交使用已保存预演中的 hash;文件验证成功后保持不可变,不得在归档后重跑命令覆盖。 + +Runtime 会在锁内重新计算事实,发生漂移就拒绝提交。成功后更新 canonical 规格,并把 change 移入日期前缀的 archive 目录。 + +遇到 canonical 冲突时,重读并改写完整目标规格,再运行 `comet native spec rebase --summary <摘要>`。该命令会受控回到 Build;随后重新实现、确认、验证和归档。未完成事务按恢复参考处理。 + +## 不变规则 + +- 不直接编辑 `phase`、`approval`、`spec_changes`、Run state、trajectory、锁或 transaction journal。 +- 不跳过阶段检查。Shape、Build、Verify 使用 `comet native next`;Archive 使用两步预演与提交。 +- 不调用外部 Skill。Native 主流程只依赖 Comet 自带 Runtime。 +- 不保存隐藏推理,只保存摘要、产物引用、命令结果、hash、状态变化和时间戳。 +- 不把 token、密码、私钥、连接串或其他凭据写入摘要、理由与报告。 +- 没有用户决定或 Runtime 阻塞时持续推进;有用户决定时,Sequential 模式只问一个最上游问题,Batch 模式询问本轮全部可回答问题,随后等待回答。 diff --git a/.agents/skills/comet-native/reference/artifacts.md b/.agents/skills/comet-native/reference/artifacts.md new file mode 100644 index 0000000..26167e6 --- /dev/null +++ b/.agents/skills/comet-native/reference/artifacts.md @@ -0,0 +1,221 @@ +# Native 产物参考 + +## 目录 + +```text +/.comet/config.yaml +/.comet/current-change.json # Native/Classic 共享的当前需求归属 +/comet/ + specs//spec.md + changes// + comet-state.yaml + brief.md + specs/ + verification.md + runtime/ + baseline-manifest.json + workspace.json # process-free 物理 root 身份;只作提示 + run-state.json # 通过 Native Protected Run I/O 读写 + trajectory.jsonl # 有事件数、单事件和总字节预算 + pending-action.json # 可选;仅在 Run 有待处理动作时存在 + context.md # 可选;Run context ref + artifacts.json # 可选;Run artifact refs + transition.json # 可选;未完成阶段推进日志 + checkpoint-journal.json # 可选;未完成 progress checkpoint 日志 + checkpoints/ + latest.json # 最近阶段边界 checkpoint + progress.json + manifests/.json + evidence/ + scopes/.json + snapshots/.json + allowances/.json + verifications/.json + check-receipts/.json + archive/YYYY-MM-DD-/ + runtime/ + locks/ + transactions// + transaction.json + events.jsonl +``` + +`artifact-root` 由项目配置唯一指定。Native 不使用隐藏的 change 目录,也不从其他需求目录发现状态。项目级 `.comet/config.yaml` 是持久配置例外;`root move` 中断恢复期间还可能在源/目标 artifact root 旁出现 Runtime 管理的临时 staging 或 quarantine,事务收口后会清除。它们不是第二个可写 Native change 根。 + +## 项目配置 + +```yaml +schema: comet.project.v1 +default_workflow: native +workflows: + - native +native: + artifact_root: docs + language: zh-CN + clarification_mode: sequential + snapshot: + include: + - "**/*" + exclude: [] + max_files: 10000 + max_total_bytes: 268435456 + max_duration_ms: 60000 +``` + +`clarification_mode` 控制 Native 如何组织用户决定,以及离开 Shape 前采用哪条确认契约:`sequential` 每轮询问一个最上游问题,`batch` 每轮询问所有前置条件已经确定的问题。字段缺失时使用 `sequential`。它不改变 change schema、生命周期、安全确认或调用方停点。 + +`snapshot` 定义内容快照的显式范围与资源预算。`include`/`exclude` 使用项目相对 `/` 路径以及 `*`、`**`、`?`;规范化策略及 hash 会写入新 change 的 baseline。后续 current snapshot 继续使用 baseline 策略,不能靠中途修改范围隐藏实现变化。`max_files`、`max_total_bytes` 和 `max_duration_ms` 只限制捕获工作,可按仓库规模提高;文件内容使用流式 SHA-256,不依赖 Git object hash,也没有独立的 5 MiB 单文件限制。 + +根目录迁移期间会出现 runtime 管理的 `pending_root_move`。存在该字段时普通写命令必须停止,不能自行选择旧根或新根。 + +## 当前需求归属 + +```json +{ + "schema": "comet.selection.v2", + "workflow": "native", + "change": "add-sentence-counting", + "branch": null +} +``` + +`.comet/current-change.json` 是 Native 与 Classic 共用的当前需求 selection,不是 Native change 产物。Native 的 `new` 和 `select` 会写入 `workflow: native`;Hook Router 每次只把写入交给这一个 workflow 的 Guard。selection 缺失时只有全项目恰好一个 active Comet change 才能只读推断;多个候选、失效 selection 或已归档目标都会失败关闭。 + +项目配置最多 64 KiB,selection 最多 16 KiB,change YAML 最多 256 KiB。brief 与单个拟议规格最多各 1 MiB;一个 change 最多 64 个拟议规格,contract 的 brief + specs 总读取最多 4 MiB。拟议规格目录还限制可枚举条目,`show` 的序列化载荷最多 10 MiB。超过预算时 Runtime 保留原文件并失败关闭,不以静默截断替代完整需求。 + +## Change 状态 + +```yaml +schema: comet.native.v3 +minimum_runtime_version: 3 +revision: 1 +name: add-sentence-counting +language: zh-CN +phase: shape +brief: brief.md +approval: null +approved_contract_hash: null +spec_changes: + - capability: sentence-counting + operation: create + source: specs/sentence-counting/spec.md + base_hash: null +verification_result: pending +verification_report: null +implementation_scope: null +verification_evidence: null +partial_allowance: null +archived: false +created_at: 2026-07-14 +run_id: null +``` + +不要直接编辑 Runtime 管理字段。`phase`、`revision`、`approval`、`approved_contract_hash`、`spec_changes`、operation、`base_hash`、三个 evidence ref、`run_id` 和 `archived` 都由 Runtime 管理。 + +`approval: confirmed` 表示 Runtime 已记录用户对当前共享理解的明确确认。`implicit` 只用于兼容旧 change,不代表用户已经确认;处于 Build 的旧 `implicit` change 必须确认后才能进入 Verify。`approved_contract_hash` 把 approval 绑定到当时的 brief/spec contract,contract 发生变化后也必须由用户重新确认。需要改变需求时,只更新 brief 和 `specs//spec.md`;删除 capability 使用 `comet native spec remove`,再由命令检查并推进。 + +## Brief + +`brief.md` 固定使用八个一级标题: + +```text +# Outcome +# Scope +# Non-goals +# Acceptance examples +# Constraints and invariants +# Decisions +# Open questions +# Verification expectations +``` + +前四节必须有实质内容。仍阻塞实现的问题在 Open questions 下以 `- [blocking]` 开头;普通备注不会阻塞 Shape。 + +Sequential 模式的 Open questions 同时保存一个最上游阻塞问题。Batch 模式使用 `- [blocking] Q1: <问题>`、`- [blocking] Q2: <问题>` 保存本轮全部可回答问题;该无序列表前缀是 Runtime 识别阻塞的固定格式,不能改成 Markdown 有序列表。未回答项继续保持 `[blocking]`。当前模式的全部问题处理完且完整性复核通过后,两种模式都使用 `- [blocking] CONFIRM: <确认内容>` 保存共享理解确认,明确确认前不能进入 Build。 + +问题编号只服务于当前澄清轮次。已确认答案应写入 Decisions 和完整目标规格;不要新增决策树产物,也不要把隐藏推理写入 brief。 + +## 完整目标规格 + +拟议规格固定写在 `changes//specs//spec.md`,描述归档后 capability 应有的完整行为,不写只在旧文本上成立的增量片段。每个 capability 只能出现一次操作: + +| operation | canonical 现状 | source | base_hash | +| --- | --- | --- | --- | +| `create` | 必须不存在 | 必填 | `null` | +| `replace` | 必须存在 | 必填 | 当前 canonical 文件 SHA-256 | +| `remove` | 必须存在 | 禁止 | 当前 canonical 文件 SHA-256 | + +`next` 首次发现 proposed spec 时推断 create/replace 并冻结 hash;`spec remove` 为 remove 冻结 hash。归档在锁内重新计算 hash,实际值与 `base_hash` 不同表示并发变化,必须重新读取并改写完整目标规格,再用 `spec rebase` 受控刷新基线、回到 Build 并重新验证,不能覆盖或手改 hash。 + +## Verification + +`verification.md` 固定使用六个非空一级标题: + +```text +# Acceptance evidence +# Commands and results +# Skipped checks +# Spec consistency +# Known limitations and risks +# Conclusion +``` + +保存可复核事实,不保存隐藏推理文本。未运行的检查放在 Skipped checks,失败结果不能写成 pass。 + +Runtime 最多从 brief 与拟议规格合计派生 1024 个验收项,超出就拒绝继续,不会先构造无界列表再截断。`acceptancePage` 每页最多 16 项;单项文字最多 512 UTF-8 字节、context 最多 4 项且每项最多 256 字节,整页最多 32 KiB。文字或 context 截断会显式标记,验收 ID 不会因分页或截断而丢失;cursor 绑定当前 acceptance hash,契约变化后旧 cursor 会失效。 + +`# Acceptance evidence` 下必须恰好有一个固定机器块。ID 由 Runtime 从 brief/spec 派生,通过 Build 结果或 `status --details` 返回;不要自行计算或改写。生成这个块时用 `comet native evidence format` 把条目序列化成规范文本再粘贴进去,不要手工排版 JSON——手写几乎不可能逐字节匹配规范序列化规则,会被拒绝并报 "canonical serialization" 错误: + +```text + +[ + { + "acceptance_id": "acceptance-", + "evidence_refs": [ + "src/feature.ts" + ] + }, + { + "acceptance_id": "acceptance-", + "evidence_refs": [], + "skipped_reason": "该平台当前不可用。" + } +] + +``` + +数组按 `acceptance_id` 排序,`evidence_refs` 也排序。每项只能二选一:至少一个项目相对 evidence ref,或空数组加非空 `skipped_reason`。不能同时给证据和跳过理由,也不能引用绝对路径、Native 外部路径、`.git` 或 `.env*`。 + +```text +comet native evidence format [--entries ] +``` + +把上面这份条目数组(不带 markers)以 JSON 通过 stdin 传入,或用 `--entries ` 指定一个 JSON 文件;命令输出的文本已经包含 start/end markers 和规范排序、缩进,原样粘贴到 verification.md 的 `# Acceptance evidence` 一节即可。 + +## 内容寻址证据 + +- `baseline-manifest.json`:change 创建时的有界项目快照。它只记录项目相对路径、size、内容 hash、capture provider、规范化 scope policy、实际资源预算和省略事实,不保存文件内容。Git 只用于项目文件枚举和 gitlink 边界,不作为普通文件的内容身份;普通文件始终流式计算 SHA-256。Git provider 纳入 tracked 和未被 ignore 的 untracked 文件,并把 submodule/gitlink 作为原子条目;非 Git 项目使用带前后枚举围栏的有界物理树 provider。显式排除项属于 baseline 定义之外,不伪装成 omission;创建时若策略范围内仍有省略项,`new` 会失败并清理未完成 change,同时返回实际限制与支持的配置修复路径。 + - `git-selection-changed`:等待 Git 写入稳定后重试,不能授权为 partial scope。 + - `git-enumeration-limit`:先缩小或清理项目所有范围。只有 current snapshot 返回可授权 scope,且用户接受未知尾部的具体风险时,才能按精确 hash、理由与 `--confirmed` 使用 partial 协议。 + - `physical-selection-changed` 和 `physical-enumeration-limit`:稳定或缩小项目树后重试,不能授权为 partial scope。 + - 任何情况都不能手改 evidence 或猜测未枚举路径。 +- `evidence/scopes/`:Build 离开时由 baseline、当前快照、声明产物和 contract 派生的 implementation scope。当前快照不完整时不会猜测删除;变化过多时只展开有界明细,其余由带数量与内容 hash 的 `scope-detail-overflow` 表示。scope 不完整时 Runtime 停止;用户显式接受后才生成 `allowances/`。 +- `evidence/verifications/`:Verify 结论的 envelope,绑定 Runtime 身份、change revision、contract、acceptance coverage、scope、报告 hash 和可选 check receipt。任一绑定事实变化都会 stale。 +- `evidence/check-receipts/`:`comet native check` 的内置策略结果。它只保存 policy/version、scope/snapshot 绑定、有界 issue 与计数,不保存文件内容,也不是测试完整性的证明。 +- `checkpoints/`:阶段内恢复摘要与真实产物 manifest。checkpoint 会递增 revision,但不改变 phase,也不能代替 brief、规格、scope 或 verification。 + +所有 hash ref 都由 Runtime 写入并在读取时重算。不要复制旧 ref 到新状态、手改 JSON 或把 receipt 当作 pass;`next`、status 和 Archive 会重新读取并检查新鲜度。 + +Evidence retention 只能由 doctor 显式执行,不在普通工作流中后台删除文件。只读 doctor 报告候选;`doctor --repair` 只清理 active change 中至少 30 天、每种 evidence kind 最新 32 份之外,并且能从当前 state refs 及其依赖闭包证明未引用的 snapshot、scope、allowance、verification 和 check receipt。 + +候选按 dependents-before-dependencies 排序。每个文件先在父链与身份复核后改名到同目录唯一 `.gc` quarantine,再复核并删除。中断的 quarantine 会由后续 doctor 发现;显式 repair 仅在原路径不存在且内容与身份有效时无覆盖恢复。 + +出现原文件与 quarantine 冲突、多份 quarantine、归档 change、pending transition/checkpoint、缺失依赖、损坏文档、未知目录项、symlink 或其他特殊文件时,推迟或拒绝清理。不要把 retention 当作修复损坏证据的办法。 + +Build 与 Verify 使用 inspect-then-persist:先计算并校验 contract、scope、acceptance、repair、Run 与 trajectory,再把最终证据引用写入状态和 transition。Partial Build 可先内容寻址保存候选 scope 以返回稳定 hash,但没有确认时不会生成 allowance 或推进;被后续检查阻塞的 Verify 不会留下可被误认成已提交结论的 verification evidence。 + +Run state、trajectory、checkpoint、pending action、context 和 artifact refs 只能经过 Native Protected Run I/O。读取拒绝 symlink/junction、非普通文件、路径或文件身份变化和越界,并执行打开前后复核;写入在原子提交前复核父链和目标身份。 + +当前预算为:Run state 256 KiB;trajectory 8 MiB、4096 个事件、单事件 256 KiB;checkpoint 与 pending action 各 256 KiB;context 与 artifact refs 各 1 MiB。通用 Engine 存储函数不是 Native 文件边界。 + +阶段 transition journal 最多 512 KiB,baseline manifest 的硬上限为 8 MiB。Archive/root move transaction journal 最多 256 KiB;`events.jsonl` 最多 1 MiB 或 1024 个事件,单个事件最多 16 KiB。摘要、无代码理由、partial 理由、repair override 摘要与跳过理由在持久化前执行长度校验和凭据形态脱敏;不要把 token、密码、私钥或连接串写入 workflow 证据。 diff --git a/.agents/skills/comet-native/reference/commands.md b/.agents/skills/comet-native/reference/commands.md new file mode 100644 index 0000000..2f31f2b --- /dev/null +++ b/.agents/skills/comet-native/reference/commands.md @@ -0,0 +1,124 @@ +# Native 命令参考 + +优先使用已安装的 `comet native`。若宿主环境只提供 Skill 文件,使用本 Skill 的自带 runtime: + +```text +node /scripts/comet-native-runtime.mjs [options] +``` + +两种入口的参数、stdout、stderr 和退出码相同。普通发现从当前目录向上寻找 `.comet/config.yaml` 或仓库根;生成式 launcher 可附加隐藏参数 `--project-root `。 + +## 项目与根目录 + +```text +comet native init [--root ] [--language en|zh-CN] +comet native root show +comet native root move +``` + +`artifact-root` 必须是项目内相对路径,默认值是 `docs`。`.` 生成 `/comet/`,`docs` 生成 `/docs/comet/`。`init --language` 会把项目的 Native 默认语言持久化到 `.comet/config.yaml`;后续 `new` 未显式传入 `--language` 时继承该值。再次运行 `init --language` 可以改变以后新建 change 的默认语言,不改写已有 change。已有配置拒绝冲突的 `--root`;改变根目录必须使用 `root move`,不能直接改配置。 + +## Change 管理 + +```text +comet native new [--language en|zh-CN] +comet native spec remove +comet native spec rebase --summary +comet native list [--cursor ] +comet native show +comet native status [--cursor ] +comet native status [--details [--acceptance-cursor ]] +comet native select +``` + +`new` 在配置缺失时创建默认配置和 `/docs/comet/`。完整目标规格写入 `specs//spec.md`;`next` 自动推断 create/replace 并冻结 canonical hash。删除 capability 使用 `spec remove`,不要手工编辑 `spec_changes`。 + +canonical 并发变化导致冲突时,先重读并改写完整目标规格,再用 `spec rebase` 刷新 operation/hash、回到 Build 并清除原验证结论。 + +`show` 返回状态、brief 和拟议完整规格。`status` 返回有预算的阶段、证据新鲜度、finding 摘要、checkpoint、repair 状态和 continuation。`status --details` 还会返回: + +- 最多 50 条详细 findings; +- `findingsTruncated` 标记; +- 恢复细节; +- 首个 `acceptancePage`。 + +findings 被截断时,先处理已返回项,再重新读取 details。`nextCursor` 非空时,用 `--acceptance-cursor` 逐页读取,直至为 null。acceptance cursor 只允许与具体 change 和 `--details` 同用,并绑定当前 acceptance hash。 + +`status` 与 `show` 始终只读。恢复已确认的目标 change 时显式运行 `select`,不要新增 `resume` 命令。`new` 与 `select` 都会写项目级共享 `.comet/current-change.json`,并把 `workflow` 固定为 `native`;它们不会修改 Classic change。 + +`list` 与不带 change 的 `status` 返回同一种只读分页投影,每页最多 24 个 change;`nextCursor` 非空时原样传给 `--cursor`。cursor 绑定当前完整名称集合,change 增删后旧 cursor 会明确失效,不会错位分页。最多接受 4096 个可见 change,整页序列化结果不超过 512 KiB。`show` 还会限制规格数量、单文件、累计读取和最终输出大小;超限时拒绝,不截断需求正文。 + +## 阶段内进度与内置检查 + +```text +comet native checkpoint \ + --summary \ + --next-action \ + [--artifact ]... \ + [--expect-revision ] + +comet native check +``` + +`checkpoint` 只保存同阶段摘要、下一动作和内容寻址的产物 manifest;它通过 revision/CAS 防止覆盖,不改变 phase。`check` 只允许在 Verify 且已有 implementation scope 时运行 Comet 内置的有界只读文本扫描。它不调用 Git、shell、项目脚本、外部 Skill 或任何外部进程,不接受任意命令、路径、环境或超时参数,也不修改项目文件或 change/Run/trajectory;结果、issue 计数和 scope 新鲜度会写入独立的内容寻址 receipt。检查发现问题或 stale 返回 1,但 receipt 仍会落盘。 + +```text +comet native evidence format [--entries ] +``` + +写 verification.md 的 `# Acceptance evidence` 机器块前,用这个命令把条目数组序列化成规范 Markdown 文本再粘贴,不要手工排版 JSON。默认从 stdin 读取条目 JSON,也可用 `--entries ` 指定文件;输出已包含 start/end markers、固定排序与缩进,原样粘贴即可通过 `next --result` 的规范序列化校验。 + +## 阶段推进 + +```text +comet native next --summary \ + [--confirmed] \ + [--artifact ]... \ + [--no-code-reason ] \ + [--allow-partial-scope --partial-reason --confirmed] \ + [--result pass|fail] \ + [--report ] \ + [--receipt ] \ + [--failure-category ]... \ + [--failed-check ]... \ + [--override-repair --override-summary ] + +comet native archive --dry-run +comet native archive --expect-preflight +``` + +- Shape:brief 和拟议规格通过后推进。Sequential 与 Batch 都必须取得最终共享理解确认并传 `--confirmed`。成功进入 Build 时,Runtime 会把 confirmed approval 绑定到当前 contract hash。 +- Build:重新检查 brief 和拟议规格;至少给出一个真实项目产物,或使用 `--no-code-reason`。旧 change 若仍为 `approval: implicit`,必须先确认当前共享理解;若 contract 在 approval 后变化,status/next 会要求用户重新确认当前 contract。两种情况都只有取得确认后才传 `--confirmed`。无法证明完整 scope 时,第一次调用返回 scope hash 与有界未归属明细而不推进;超出明细预算的变化由 `scope-detail-overflow` 的数量与内容 hash 表示。只有用户接受具体风险后,才可用完全匹配的 `--allow-partial-scope`、理由与 `--confirmed` 重试。 +- Verify:必须提供 `--result` 和完整 `--report`;可选 `--receipt` 必须是当前 change、revision、contract 与 implementation scope 上 fresh 的内置 receipt。fail 回到 Build,可用失败分类和检查 ID 形成无进展签名;pass 进入 Archive。 +- Repair:第三次相同失败会返回 manual stop。scope 真正变化时普通 Build `next` 会结束旧 repair episode 并继续;scope 不变时只能用 status 返回的 signature 和非空摘要 override 一次。单个 episode 的 semantic repair budget 与已耗尽 override 不可绕过;通用 Run iteration 只提供事件序号,不是长期 change 的永久停止条件。 +- Archive:只能由 `archive` 命令完成,不能用 `next` 代替。先 `--dry-run`,再把同一次预演返回的 `preflightHash` 原样传给 `--expect-preflight`;Runtime 在锁内重算后才提交。 + +## 诊断与恢复 + +```text +comet native doctor [] +comet native doctor [] --repair +comet native doctor [] --repair [--strategy continue|rollback] +``` + +只读 doctor 不改文件。`--repair` 只处理可证明安全的 selection、陈旧锁、evidence retention、普通阶段 transition、workspace 身份修复和确定性事务恢复;用户编写的 YAML、Markdown 与规格不会被自动重写。 + +`--strategy` 是可选的事务恢复参数,不是普通 repair 的必填项。普通 transition 只支持 `continue`,不支持 `rollback`。 + +doctor 也会只读报告 evidence retention 候选。显式 `--repair` 只清理 active change 中至少 30 天、每种 evidence kind 最新 32 份之外、且依赖闭包证明未引用的派生 evidence/receipt;归档证据、当前状态引用、依赖项、较新文件和每类最新 32 份始终保留。删除按 dependents-before-dependencies 排序,并先进入同目录 quarantine;中断后只读 doctor 报告 recovery required,显式 repair 在无覆盖且身份匹配时恢复。存在 pending journal、损坏、原文件与 quarantine 冲突或未知/特殊文件时 fail closed,不为腾空间冒险删除。 + +普通 `new`、`next`、`archive`、`root move` 等写命令不会自动接管陈旧锁。只有显式 `doctor --repair` 会在证明本机 owner 已不存在、锁身份未变化且没有相冲突恢复事务时接管;活动锁和无法证明陈旧的锁始终保留。 + +## 输出与退出码 + +所有命令支持 `--json`。JSON 模式只输出一个对象,包含 `command`、`exitCode`、`data`,失败时还包含结构化 `error`。 + +| 退出码 | 含义 | +| --- | --- | +| `0` | 成功 | +| `1` | 内置 `check` 完成但发现问题或结果 stale | +| `64` | 参数或用法错误 | +| `65` | 配置、状态或产物无效 | +| `73` | 锁、事务、并发 hash 或根目录冲突 | +| `75` | repair stagnation 或 hard stop 阻塞继续 | +| `70` | 未预期的内部失败 | diff --git a/.agents/skills/comet-native/reference/recovery.md b/.agents/skills/comet-native/reference/recovery.md new file mode 100644 index 0000000..5d7e552 --- /dev/null +++ b/.agents/skills/comet-native/reference/recovery.md @@ -0,0 +1,143 @@ +# Native 恢复参考 + +## 上下文恢复顺序 + +每次恢复都从磁盘事实开始: + +1. 读取项目 `.comet/config.yaml`,确认唯一 artifact root 和 `native.clarification_mode`;该字段缺失时使用 `sequential`。若有 `pending_root_move`,先运行 doctor。 +2. 运行只读的 `comet native status`;多个 active change 时读取项目级共享 `.comet/current-change.json`,确认 `workflow: native` 和目标 change,或让用户明确选择。 +3. 对目标 change 运行只读的 `show` 和 `status --details`,读取 `comet-state.yaml`、brief、拟议完整规格、verification、有界结构化 findings、`findingsTruncated` 标记和最新 checkpoint。若 findings 被截断,先处理已返回项,再重新读取 details;Verify/Archive 的验收 ID 则按 `acceptancePage.nextCursor` 独立分页取得,不依赖丢失的旧响应。 +4. 目标确认后运行 `comet native select ` 建立共享 selection;没有单独的 `resume` 命令,且只读命令不会隐式选择 change。 +5. 读取相关 canonical 规格、实现、规则、测试和当前工作区状态。 +6. 根据 phase 执行 Shape、Build、Verify 或 Archive,不依赖聊天记录猜阶段。 + +状态、Run state、trajectory 或 transaction journal 畸形时停止写入并运行只读 doctor。不要通过手工改 phase 来绕过问题。 + +## 澄清轮次恢复 + +Shape 或 Build 存在 `[blocking]` 时,从 brief 的 Open questions 恢复当前未决事项,不依赖聊天记录重建答案。Sequential 模式恢复一个最上游问题;Batch 模式按已保存编号恢复本轮全部可回答问题。切换配置不会消除已有阻塞项:先把用户答案对应回已保存问题,再按当前 `clarification_mode` 计算下一轮。 + +能从仓库、工具或运行环境查明的事实继续由 Agent 调查。宿主支持并行工作时可以并行查证,但恢复不能依赖任何可选的并行能力;调查中的事实只延后依赖它的问题。 + +Sequential 模式恢复当前最上游问题,Batch 模式恢复所有仍未回答的编号问题;两者都继续保持 `[blocking]`。全部问题解决后仍要恢复或建立最终共享理解确认;只有用户明确确认后,才能移除该阻塞项并进入 Build。该过程不增加新 phase、change 状态字段或独立决策树文件。 + +长任务在同一 phase 内中断前可写 checkpoint: + +```text +comet native checkpoint \ + --summary <已完成事实> \ + --next-action <下一动作> \ + [--artifact <项目相对路径>] \ + [--expect-revision ] +``` + +恢复时先检查 checkpoint 的 freshness。phase、revision 或 manifest 已变化时把它当历史提示,不把旧下一动作强行应用到新状态;Runtime 会在 details 中显式报告 stale 原因。 + +## 普通阶段推进 + +`next` 在 change 的 `runtime/transition.json` 先写入 prepared journal,再更新 Run state、`comet-state.yaml`、trajectory 和 checkpoint。全部完成后才删除 journal。 + +`status` 和 doctor 会报告未完成 transition。再次运行 `next` 或进入 Archive 时,runtime 会先确定性续做;也可以显式执行: + +```text +comet native doctor --repair --strategy continue +``` + +普通阶段推进没有 canonical 文件副作用,因此只支持 `continue`,不支持 `rollback`。journal 畸形时保留原文件并停止,不手工拼接状态。 + +## Baseline 缺失或不完整 + +`new` 要求 baseline 完整。Git 项目只评估 tracked 与未被 ignore 的 untracked 文件;ignored 缓存和嵌套仓库内容不会制造省略。 + +若 Runtime 返回 `baseline-snapshot-missing` 或 `baseline-snapshot-incomplete`,不要用当前文件重建 baseline,也不要把它当成 doctor 可自动修复的问题。这会丢失 change 创建以来的历史差异。 + +只能从可信备份恢复原 baseline,或保留用户编写的 brief、规格和实现事实后新建 change,重新建立完整 baseline。 + +## 证据失效与受控回退 + +进入 Verify 后,若 brief、拟议规格或项目快照变化,status 会先把失效的 implementation scope 指向受控回退;运行返回的仅含摘要的 `next` 命令回到 Build,再为变化后的 contract 取得重新确认并生成新 scope。进入 Archive 后,implementation scope、verification report 或 check receipt 等任一绑定事实变化也会触发同一回退。不要删除 finding、沿用旧 pass 或手工替换 hash ref。 + +Archive 必须使用两步预演,不能把一次旧的 ready 判断当作提交授权: + +```text +comet native archive --dry-run +comet native archive --expect-preflight <刚返回的-sha256> +``` + +第二步在锁内重算 contract、canonical base、scope、verification、当前 root 冲突与恢复状态;任何变化都会拒绝并要求重新预演。 + +## Verify fail 与修复停止 + +Verify fail 会诚实回到 Build。提交稳定、非敏感的 `--failure-category` 和 `--failed-check` 后,Runtime 先校验 token、数量与边界,再以 failure + contract + scope 形成签名:同签名第二次告警,第三次且 scope 无进展时 manual stop,单个 episode 累计到语义上限时 hard stop。 + +- implementation scope 真正变化:说明已有机械进展,普通 Build `next` 结束旧 episode 并自动开始新一轮;之前的 hard stop 不会锁死新实现。 +- scope 未变化但有一个明确新假设:只能使用 status 返回的 signature 和非空摘要做一次 `--override-repair`。 +- 同一签名已 override、或达到 hard stop:不能弱化验证或伪造 pass;保留现场并请用户决定范围、约束或是否停止。 + +一次 pass 会结束当前 repair episode;之后若旧 Archive 证据失效并重新出现相同 failure,它是新的 episode,但原 trajectory 仍保留审计事实。通用 Engine iteration budget 不参与这个产品语义。 + +## Canonical spec 冲突 + +若另一个 change 在当前 change 冻结 `base_hash` 后改变了同一 canonical spec,Archive 会停止。不要手改 hash: + +1. 重读最新 canonical spec、brief 和拟议完整规格; +2. 按用户意图改写完整目标规格,必要时先解决一个用户决定; +3. 运行 `comet native spec rebase --summary <摘要>`; +4. runtime 刷新 operation/hash,把 change 受控重开到 Build,并清除旧验证结论; +5. 重新实现、在需要时用 `--confirmed` 记录刚确认的决定、重新 Verify 和 Archive。 + +若 remove 的目标已经被并发 change 删除,rebase 会移除已满足的 remove 意图;其他 remove 会冻结最新 canonical hash 后重新验证。 + +## 当前工作区内的并行提示 + +status/Archive 会比较当前 Native root 中可见 change 的 capability、operation、base hash 和声明产物:确定冲突必须先解决;可能重叠也会在归档前阻塞。它不能看到未集成 worktree、远端分支或其他机器,因此不是分布式锁。 + +`workspace-root-changed` 和 `workspace-inspection-unavailable` 是显式 advisory,只用于解释当前 root 的事实来源,不单独阻止推进或归档。finding 会列出 `native-root-ref`、`project-root-path`、`native-root-path` 等具体漂移组件。Native 默认不读取 Git branch、HEAD 或 worktree changed paths。 + +不要把任意 `workspace-*` 都当成提示。未知 workspace 完整性 finding 仍按错误处理;Runtime 要求修复 workspace 身份时,先运行只读 doctor,再按报告执行显式 `doctor --repair`。 + +## Archive 事务 + +Archive 使用全局锁、staged specs、逐操作事件日志和备份。中断时 canonical 树可能处于事务中间状态,但 journal 会保留未完成事实。 + +Archive 的 stage、backup、apply 与 rollback copy 都通过受保护句柄读取:打开前后复核源文件、realpath、父目录身份、大小与预期 hash,目标在原子提交前再次检查。它不会因为事前路径包含检查通过,就信任后续路径仍指向同一文件。 + +write/remove 会先把 canonical 原对象身份和原内容绑定进事务,再原子改名到同目录隔离位并复验;write 使用事务私有 candidate 和无覆盖安装,rollback 也必须先隔离并验证 post 对象,再无覆盖恢复 original。即使并发替换后的内容 hash 恰好相同,只要它已经是另一个文件对象,Runtime 仍会拒绝覆盖并保留现场。 + +`events.jsonl` 读取有总字节、事件数量和单事件预算。崩溃若只留下最后一个 canonical JSON 事件的可证明前缀,Runtime 会在下一次 append 前按原始 bytes 的 hash/size 做 CAS,并原子重写到最后一个完整事件;中间坏行、完整但非法的尾行、非规范 JSON 尾部或并发改写一律 fail closed。完整但没有末尾换行的事件不会被误删,重复操作按 `type + operationId` 保持 exactly-once。 + +```text +comet native doctor +comet native doctor --repair --strategy continue +comet native doctor --repair --strategy rollback +``` + +- `continue`:从最后一个已完成操作继续,收敛到 committed archive。 +- `rollback`:按逆序恢复 canonical 文件和 active change。 +- Runtime 会先验证已移动的 archive tree、最终 state、受保护 Run、trajectory 事件和完成决定,随后才写 `archive-finalization-started`。标记写入前仍可安全 rollback;标记写入后已经跨越不可回滚边界,只能 continue,避免产生“已完成证据但又恢复为 active”的矛盾状态。 + +先阅读 doctor 的路径、transaction id 和冲突信息。若 hash 与 journal 两端都不一致,保留所有树并停止自动修复。 + +## Artifact root 迁移 + +`root move` 依次经过 `copying`、`ready`、`switched`。配置中的 `pending_root_move` 是恢复事实源;存在时普通 Native 写命令会失败关闭。 + +- `copying`:旧 root 是当前根,目标 staging 可能不完整。 +- `ready`:staging 已通过逐文件路径、大小和 SHA-256 校验,尚未切换配置。 +- `switched`:配置已指向新 root,旧 root 只有在再次验证两棵树等价、父链与目录身份后,才会先改名到事务 ID 绑定的 sibling quarantine,再复核删除。中断时 continue/rollback 会识别该 quarantine 并确定性收口,不会按旧路径直接递归删除。 + +使用 doctor 的显式 continue 或 rollback。若两棵树 hash 不一致,不删除任何一棵,并把报告中的两条路径交给用户处理。 + +## 锁与安全修复 + +doctor 区分活动锁、可证明陈旧的本机锁和无法判断的远端锁。只在 owner 进程确定不存在且没有未处理事务依赖时删除陈旧锁;不自动破坏活动或未知锁。 + +锁同时绑定 owner 元数据、锁文件身份和进程内 FIFO。普通 mutation 不会因为看到“看似过期”的时间戳就自动恢复;只有显式 doctor repair 才能执行带身份复验的 takeover,避免旧 owner 在新 owner 获锁后删除新锁而形成 split-brain。 + +doctor 可以安全清理指向不存在 change 的 selection。它不会自动重写损坏的配置、change YAML、brief、规格或 verification;这些内容必须根据用户意图人工修正后重新检查。 + +Evidence retention 遵守显式修复边界。默认 doctor 只报告候选;`--repair` 仅删除至少 30 天、每种 evidence kind 最新 32 份之外,并且依赖闭包证明未引用的 active-change 派生 evidence/receipt。 + +删除按 dependents-before-dependencies 排序,并先改名到同目录唯一 `.gc` quarantine。若在最终删除前中断,后续只读 doctor 报告 `evidence-retention-recovery-required`;显式 repair 仅在原路径不存在、quarantine 内容与身份有效时无覆盖恢复。 + +出现原文件与 quarantine 冲突、多份 quarantine、归档证据、pending 恢复、缺失依赖、损坏文档、未知目录项、symlink 或其他特殊文件时,推迟清理并失败关闭。 diff --git a/.agents/skills/comet-native/scripts/comet-native-hook-guard.mjs b/.agents/skills/comet-native/scripts/comet-native-hook-guard.mjs new file mode 100644 index 0000000..d7cc167 --- /dev/null +++ b/.agents/skills/comet-native/scripts/comet-native-hook-guard.mjs @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import { main } from './comet-native-runtime.mjs'; + +process.exitCode = await main(['hook-guard', ...process.argv.slice(2)]); diff --git a/.agents/skills/comet-native/scripts/comet-native-runtime.mjs b/.agents/skills/comet-native/scripts/comet-native-runtime.mjs new file mode 100644 index 0000000..bd78ee8 --- /dev/null +++ b/.agents/skills/comet-native/scripts/comet-native-runtime.mjs @@ -0,0 +1,29563 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path48) { + const ctrl = callVisitor(key, node, visitor, path48); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path48, ctrl); + return visit_(key, ctrl, visitor, path48); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path48 = Object.freeze(path48.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path48); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path48 = Object.freeze(path48.concat(node)); + const ck = visit_("key", node.key, visitor, path48); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path48); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path48) { + const ctrl = await callVisitor(key, node, visitor, path48); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path48, ctrl); + return visitAsync_(key, ctrl, visitor, path48); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path48 = Object.freeze(path48.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path48); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path48 = Object.freeze(path48.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path48); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path48); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path48) { + if (typeof visitor === "function") + return visitor(key, node, path48); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path48); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path48); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path48); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path48); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path48); + return void 0; + } + function replaceNode(key, path48, node) { + const parent = path48[path48.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path48, value) { + let v = value; + for (let i = path48.length - 1; i >= 0; --i) { + const k = path48[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path48) => path48 == null || typeof path48 === "object" && !!path48[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path48, value) { + if (isEmptyPath(path48)) + this.add(value); + else { + const [key, ...rest] = path48; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path48) { + const [key, ...rest] = path48; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path48, keepScalar) { + const [key, ...rest] = path48; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path48) { + const [key, ...rest] = path48; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path48, value) { + const [key, ...rest] = path48; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify4(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify4.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify4.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify4 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify4.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify5 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify5(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify4.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify4.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp2 = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp2.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp2 = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp2.intTime, + timestamp2.floatTime, + timestamp2.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp2 = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp2.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp2.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp2.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp2.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify4.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify4.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify4.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path48, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path48, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path48) { + if (Collection.isEmptyPath(path48)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path48) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path48, keepScalar) { + if (Collection.isEmptyPath(path48)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path48, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path48) { + if (Collection.isEmptyPath(path48)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path48) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path48, value) { + if (Collection.isEmptyPath(path48)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path48), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path48, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify4 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path48) => { + let item = cst; + for (const [field2, index] of path48) { + const tok = item?.[field2]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path48) => { + const parent = visit.itemAtPath(cst, path48.slice(0, -1)); + const field2 = path48[path48.length - 1][0]; + const coll = parent?.[field2]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path48, item, visitor) { + let ctrl = visitor(item, path48); + if (typeof ctrl === "symbol") + return ctrl; + for (const field2 of ["key", "value"]) { + const token = item[field2]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path48.concat([[field2, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field2 === "key") + ctrl = ctrl(item, path48); + } + } + return typeof ctrl === "function" ? ctrl(item, path48) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs34 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs34, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs34); + } else { + Object.assign(it, { key: fs34, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs34 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs34, sep: [] }); + else if (it.sep) + this.stack.push(fs34); + else + Object.assign(it, { key: fs34, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument5(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument5(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify4(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument5; + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/comet-native/native-cli-entry.ts +import { pathToFileURL } from "url"; + +// domains/comet-native/native-cli.ts +import path47 from "path"; + +// platform/fs/race-safe-read.ts +import { constants as fsConstants, promises as fs } from "fs"; + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function hasComparableFileObject(left, right) { + return hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev) && hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// platform/fs/race-safe-read.ts +var RaceSafeReadError = class extends Error { + reason; + constructor(reason, message, options) { + super(message, options); + this.name = "RaceSafeReadError"; + this.reason = reason; + } +}; +function birthtimeOf(stat) { + return "birthtimeNs" in stat && typeof stat.birthtimeNs === "bigint" ? stat.birthtimeNs : stat.birthtimeMs; +} +function ctimeOf(stat) { + return "ctimeNs" in stat && typeof stat.ctimeNs === "bigint" ? stat.ctimeNs : stat.ctimeMs; +} +function identityOf(stat) { + return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) }; +} +function sameStatIdentity(left, right) { + const leftObject = identityOf(left); + const rightObject = identityOf(right); + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && birthtimeOf(left) === birthtimeOf(right) && ctimeOf(left) === ctimeOf(right) && left.size === right.size; +} +async function readFileRaceSafe(file, maxBytes, options = {}) { + if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) { + throw new Error("race-safe read byte limit must be a positive integer"); + } + const label = options.label ?? "file"; + const bigint = options.bigint === true; + const before = await fs.lstat(file, { bigint }); + if (!before.isFile() || before.isSymbolicLink()) { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`); + } + if (BigInt(before.size) > BigInt(maxBytes)) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + const beforeRealPath = await fs.realpath(file); + await options.verify?.("pre-open", { realPath: beforeRealPath, identity: identityOf(before) }); + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + let handle; + try { + handle = await fs.open(file, flags); + } catch (error) { + if (error.code === "ELOOP") { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`, { + cause: error + }); + } + throw error; + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat({ bigint }), + fs.lstat(file, { bigint }), + fs.realpath(file) + ]); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameStatIdentity(before, opened) || !sameStatIdentity(before, pathAfterOpen)) { + throw new RaceSafeReadError("changed", `${label} changed while opening`); + } + await options.verify?.("post-open", { + realPath: realPathAfterOpen, + identity: identityOf(opened) + }); + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + for (; ; ) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat({ bigint }), + fs.lstat(file, { bigint }), + fs.realpath(file) + ]); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameStatIdentity(before, afterHandle) || !sameStatIdentity(before, afterPath)) { + throw new RaceSafeReadError("changed", `${label} changed while reading`); + } + await options.verify?.("post-read", { + realPath: afterRealPath, + identity: identityOf(afterHandle) + }); + return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-archive.ts +import { randomUUID as randomUUID6 } from "crypto"; +import { promises as fs23 } from "fs"; +import path34 from "path"; +import { isDeepStrictEqual as isDeepStrictEqual2 } from "util"; + +// domains/engine/loop.ts +import { createHash } from "crypto"; + +// domains/engine/guardrails.ts +function checkAction(action, state, guardrails, confirmations) { + if (state.iteration >= guardrails.maxIterations) { + return { allowed: false, reason: `Iteration budget exhausted: ${guardrails.maxIterations}` }; + } + if (action.type === "invoke_skill" && !guardrails.allowedSkills.includes(action.ref ?? "")) { + return { allowed: false, reason: `Skill is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.type === "call_tool" && !guardrails.allowedTools.includes(action.ref ?? "")) { + return { allowed: false, reason: `Tool is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.type === "handoff" && !guardrails.allowedAgents.includes(action.ref ?? "")) { + return { allowed: false, reason: `Agent is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.ref && guardrails.confirmationRequiredFor.includes(action.ref) && !confirmations.has(action.ref)) { + return { allowed: false, reason: `User confirmation required for: ${action.ref}` }; + } + const retries2 = state.retries[action.id] ?? 0; + if (retries2 > guardrails.maxRetriesPerAction) { + return { allowed: false, reason: `Retry budget exhausted for action: ${action.id}` }; + } + return { allowed: true }; +} + +// domains/engine/resolver.ts +function deepFreeze(value) { + if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value; + for (const key of Reflect.ownKeys(value)) { + deepFreeze(value[key]); + } + return Object.freeze(value); +} +function readonlyCopy(value) { + return deepFreeze(structuredClone(value)); +} +function resolveDeterministicStep(resolver, pkg, state, context) { + return resolver.resolveStep({ + pkg: readonlyCopy(pkg), + state: readonlyCopy(state), + context: readonlyCopy(context) + }); +} +function resolveDeterministicNext(resolver, pkg, state, step, outcome, context) { + return resolver.resolveNext({ + pkg: readonlyCopy(pkg), + state: readonlyCopy(state), + step: readonlyCopy(step), + outcome: readonlyCopy(outcome), + context: readonlyCopy(context) + }); +} + +// domains/engine/loop.ts +function actionId(runId, iteration, stepId) { + return createHash("sha256").update(`${runId}:${iteration}:${stepId ?? "adaptive"}`).digest("hex").slice(0, 16); +} +function stepFor(pkg, id) { + return pkg.definition.orchestration.steps?.find((step) => step.id === id); +} +function decideWithResolver(pkg, state, confirmations, resolver, context) { + if (state.status !== "running") return { state, action: null, reason: `Run is ${state.status}` }; + if (state.pending) + return { state, action: null, reason: `Action already pending: ${state.pending}` }; + if (state.orchestration === "adaptive") { + return { state, action: null, reason: "Adaptive orchestration requires an Agent candidate" }; + } + const resolvedStep = resolveDeterministicStep(resolver, pkg, state, context); + if (!resolvedStep && state.currentStep === null) { + return { state: { ...state, status: "completed" }, action: null }; + } + if (!resolvedStep) { + return { + state: { ...state, status: "failed" }, + action: null, + reason: `Unknown current step: ${state.currentStep}` + }; + } + const step = stepFor(pkg, resolvedStep.id); + if (!step) { + return { + state: { ...state, status: "failed" }, + action: null, + reason: `Resolver returned unknown current step: ${resolvedStep.id}` + }; + } + const resolvedState = state.currentStep === step.id ? state : { ...state, currentStep: step.id }; + const action = { + ...step.action, + id: actionId(resolvedState.runId, resolvedState.iteration, step.id), + stepId: step.id + }; + return acceptAction(pkg, resolvedState, action, confirmations); +} +function acceptAction(pkg, state, action, confirmations) { + const guard = checkAction(action, state, pkg.guardrails, confirmations); + if (!guard.allowed) return { state, action: null, reason: guard.reason }; + return { state: { ...state, pending: action.id, status: "waiting" }, action }; +} +function recordOutcomeWithResolver(pkg, state, outcome, resolver, context) { + if (!state.pending || state.pending !== outcome.actionId) { + throw new Error(`Outcome does not match pending action: ${outcome.actionId}`); + } + const resolvedStep = state.orchestration === "deterministic" ? resolveDeterministicStep(resolver, pkg, state, context) : void 0; + const step = resolvedStep ? stepFor(pkg, resolvedStep.id) : void 0; + if (state.orchestration === "deterministic" && !resolvedStep) { + throw new Error(`Unknown current step: ${state.currentStep ?? "(missing)"}`); + } + if (state.orchestration === "deterministic" && !step) { + throw new Error(`Resolver returned unknown current step: ${resolvedStep.id}`); + } + if (outcome.status === "failed") { + const retries2 = { + ...state.retries, + [outcome.actionId]: (state.retries[outcome.actionId] ?? 0) + 1 + }; + return { ...state, pending: null, status: "running", retries: retries2 }; + } + const next = state.orchestration === "deterministic" ? resolveDeterministicNext(resolver, pkg, state, step, outcome, context) : state.currentStep; + if (next !== null && state.orchestration === "deterministic" && !stepFor(pkg, next)) { + throw new Error(`Resolver returned unknown next step: ${next}`); + } + return { + ...state, + currentStep: next, + iteration: state.iteration + 1, + pending: null, + status: next === null && state.orchestration === "deterministic" ? "completed" : "running" + }; +} + +// domains/comet-native/native-artifacts.ts +import { promises as fs14 } from "fs"; +import path17 from "path"; + +// domains/comet-native/native-change.ts +var import_yaml3 = __toESM(require_dist(), 1); +import { promises as fs13 } from "fs"; +import path16 from "path"; + +// domains/comet-native/native-bounded-file.ts +import { createHash as createHash2 } from "node:crypto"; +import { promises as fs2 } from "node:fs"; +import path2 from "node:path"; +import { TextDecoder } from "node:util"; + +// domains/comet-native/native-sensitive-paths.ts +import path from "path"; +var NATIVE_EXCLUDED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([ + ".cache", + ".git", + ".gradle", + ".gnupg", + ".mypy_cache", + ".next", + ".npm", + ".pnpm-store", + ".pytest_cache", + ".ssh", + ".turbo", + ".venv", + ".yarn", + "__pycache__", + "node_modules", + "venv" +]); +var NATIVE_SENSITIVE_FILE_NAMES = /* @__PURE__ */ new Set([ + ".git-credentials", + ".netrc", + ".npmrc", + ".pypirc", + "auth.json", + "credentials.json" +]); +function isNativeEnvFileName(name) { + return name.toLowerCase().startsWith(".env"); +} +function nativeSensitiveRelativePathReason(relativeRef) { + const segments = relativeRef.replaceAll("\\", "/").split("/").filter(Boolean); + const lower = segments.map((segment) => segment.toLowerCase()); + if (lower.some((segment) => isNativeEnvFileName(segment))) return "environment-file"; + if (lower.some((segment) => NATIVE_SENSITIVE_FILE_NAMES.has(segment))) { + return "credential-config"; + } + if (lower.includes(".git")) return "git-metadata"; + if (lower.some((segment) => NATIVE_EXCLUDED_DIRECTORY_NAMES.has(segment))) { + return "dependency-or-cache"; + } + if (lower.join("/") === ".comet/config.yaml") { + return "comet-config"; + } + if (lower.join("/") === ".comet/current-change.json") { + return "comet-selection"; + } + return null; +} +function nativeSensitiveArtifactReason(paths, relativeRef) { + const generic = nativeSensitiveRelativePathReason(relativeRef); + if (generic) return generic; + const target = path.resolve(paths.projectRoot, ...relativeRef.split("/")); + const relativeNativeRoot = path.relative(paths.projectRoot, paths.nativeRoot).replaceAll("\\", "/"); + const normalized = relativeRef.replaceAll("\\", "/"); + if (normalized === relativeNativeRoot || normalized.startsWith(`${relativeNativeRoot}/`) || target === path.resolve(paths.configFile)) { + return "native-runtime"; + } + return null; +} + +// domains/comet-native/native-bounded-file.ts +var DEFAULT_NATIVE_ARTIFACT_MAX_BYTES = 1024 * 1024; +function isInside(parent, target) { + const relative = path2.relative(parent, target); + return relative === "" || !path2.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path2.sep}`); +} +function portableArtifactRef(value) { + const normalized = path2.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path2.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`Native artifact ref must be normalized and relative: ${value}`); + } + const lower = value.toLowerCase(); + const sensitiveReason = nativeSensitiveRelativePathReason(value); + if (sensitiveReason || lower === "runtime" || lower.startsWith("runtime/")) { + throw new Error( + `Native artifact ref is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${value}` + ); + } + return value; +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native artifact byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function directoryIdentity(directory) { + const stat = await fs2.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native artifact parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs2.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory) { + const lexicalRoot = path2.resolve(root); + const lexicalDirectory = path2.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error("Native artifact path is outside its root"); + } + const chain = [await directoryIdentity(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path2.relative(lexicalRoot, lexicalDirectory).split(path2.sep).filter(Boolean)) { + cursor = path2.join(cursor, segment); + const identity = await directoryIdentity(cursor); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`Native artifact parent resolves outside its root: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain(chain) { + for (const identity of chain) { + const stat = await fs2.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs2.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native artifact parent changed while reading: ${identity.path}`); + } + } +} +async function readNativeBoundedTextFile(options) { + const ref = portableArtifactRef(options.ref); + const maxBytes = positiveLimit(options.maxBytes ?? DEFAULT_NATIVE_ARTIFACT_MAX_BYTES); + const file = path2.resolve(options.root, ...ref.split("/")); + const chain = await captureDirectoryChain(options.root, path2.dirname(file)); + await options.hooks?.afterParentChainCaptured?.(); + const before = await fs2.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native artifact must be a regular file: ${ref}`); + } + if (before.size > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + const realPath = await fs2.realpath(file); + if (!isInside(chain[0].realPath, realPath)) { + throw new Error(`Native artifact resolves outside its root: ${ref}`); + } + const handle = await fs2.open(file, "r"); + try { + const [opened, afterOpenPath, afterOpenRealPath] = await Promise.all([ + handle.stat(), + fs2.lstat(file), + fs2.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!opened.isFile() || !afterOpenPath.isFile() || afterOpenPath.isSymbolicLink() || afterOpenRealPath !== realPath || !sameFileIdentity(before, opened) || !sameFileIdentity(opened, afterOpenPath)) { + throw new Error(`Native artifact changed while opening: ${ref}`); + } + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (result2.bytesRead === 0) break; + total += result2.bytesRead; + if (total > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + chunks.push(Buffer.from(buffer.subarray(0, result2.bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs2.lstat(file), + fs2.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity(opened, afterHandle) || !sameFileIdentity(opened, afterPath)) { + throw new Error(`Native artifact changed while reading: ${ref}`); + } + const bytes = Buffer.concat(chunks, total); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`Native artifact is not valid UTF-8: ${ref}`, { cause: error }); + } + return { + ref, + size: total, + hash: createHash2("sha256").update(bytes).digest("hex"), + text + }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-atomic-file.ts +import { randomUUID } from "crypto"; +import { promises as fs3 } from "fs"; +import path3 from "path"; +function isInside2(parent, target) { + const relative = path3.relative(parent, target); + return relative === "" || !path3.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path3.sep}`); +} +function sameDirectoryIdentity2(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity2(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryIdentity(directory) { + const stat = await fs3.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native atomic write parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs3.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function verifyDirectoryChain2(chain) { + for (const identity of chain) { + const stat = await fs3.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity2(identity, stat) || await fs3.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native atomic write parent changed before commit: ${identity.path}`); + } + } +} +async function prepareContainedDirectoryChain(root, directory) { + const lexicalRoot = path3.resolve(root); + const lexicalDirectory = path3.resolve(directory); + if (!isInside2(lexicalRoot, lexicalDirectory)) { + throw new Error(`Native atomic write parent is outside its managed root: ${directory}`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot)]; + const segments = path3.relative(lexicalRoot, lexicalDirectory).split(path3.sep).filter(Boolean); + let cursor = lexicalRoot; + for (const segment of segments) { + await verifyDirectoryChain2(chain); + cursor = path3.join(cursor, segment); + try { + await fs3.mkdir(cursor); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const identity = await captureDirectoryIdentity(cursor); + if (!isInside2(chain[0].realPath, identity.realPath)) { + throw new Error(`Native atomic write parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain2(chain); + return chain; +} +async function syncDirectory(directory) { + let handle; + try { + handle = await fs3.open(directory, "r"); + await handle.sync(); + } catch (error) { + const code = error.code; + if (!["EACCES", "EBADF", "EINVAL", "EISDIR", "ENOTSUP", "EPERM"].includes(code ?? "")) { + throw error; + } + } finally { + await handle?.close(); + } +} +async function atomicWrite(file, content, options = {}) { + const directory = path3.dirname(file); + const directoryChain = options.containedRoot ? await prepareContainedDirectoryChain(options.containedRoot, directory) : null; + if (!directoryChain) await fs3.mkdir(directory, { recursive: true }); + const temporary = path3.join(directory, `.${path3.basename(file)}.${randomUUID()}.tmp`); + let handle; + let temporaryIdentity; + try { + await options.beforeTemporaryOpen?.(); + handle = await fs3.open(temporary, "wx"); + temporaryIdentity = await handle.stat(); + if (directoryChain) { + const [temporaryPathStat, temporaryRealPath] = await Promise.all([ + fs3.lstat(temporary), + fs3.realpath(temporary) + ]); + await verifyDirectoryChain2(directoryChain); + if (!temporaryPathStat.isFile() || temporaryPathStat.isSymbolicLink() || !sameFileIdentity2(temporaryIdentity, temporaryPathStat) || !isInside2(directoryChain[0].realPath, temporaryRealPath)) { + throw new Error("Native atomic write temporary file opened outside its managed parent"); + } + } + if (typeof content === "string") await handle.writeFile(content, "utf8"); + else await handle.writeFile(content); + await handle.sync(); + if (!sameFileIdentity2(temporaryIdentity, await handle.stat())) { + throw new Error("Native atomic write temporary file changed while writing"); + } + await handle.close(); + handle = void 0; + await options.beforeCommit?.(); + if (directoryChain) { + await verifyDirectoryChain2(directoryChain); + const temporaryStat = await fs3.lstat(temporary); + if (!temporaryStat.isFile() || temporaryStat.isSymbolicLink() || !temporaryIdentity || !sameFileIdentity2(temporaryStat, temporaryIdentity)) { + throw new Error("Native atomic write temporary file changed before commit"); + } + } + if (options.exclusive) { + await fs3.link(temporary, file); + await fs3.unlink(temporary); + } else { + await fs3.rename(temporary, file); + } + await syncDirectory(directory); + } catch (error) { + await handle?.close(); + if (!directoryChain) { + await fs3.rm(temporary, { force: true }); + } else { + try { + await verifyDirectoryChain2(directoryChain); + await fs3.rm(temporary, { force: true }); + } catch { + } + } + throw error; + } +} +async function atomicWriteText(file, content, options = {}) { + await atomicWrite(file, content, options); +} +async function atomicWriteBytes(file, content, options = {}) { + await atomicWrite(file, content, options); +} +async function atomicWriteJson(file, value, options = {}) { + await atomicWriteText(file, JSON.stringify(value, null, 2) + "\n", options); +} + +// domains/comet-native/native-config.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { promises as fs6 } from "fs"; +import path6 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml = __toESM(require_dist(), 1); +var COMMENTS = { + en: { + schema: "# Configuration schema used by Comet. Do not edit this value.", + default_workflow: "# Default workflow entered by /comet. Must also appear in workflows.", + workflows: "# Workflows enabled in this project: native, classic, or both.", + ambient_resume: "# Enables automatic recovery through the read-only Ambient Resume probe for both Native and Classic. Set false to disable it.\n# ambient_resume: true | false", + native: "# Native workflow settings. They do not change Classic state or behavior.", + "native.artifact_root": "# Root directory where Native stores Comet specs, changes, and runtime data.", + "native.language": "# Artifact language used by Native workflow documents.\n# language: en | zh-CN", + "native.clarification_mode": "# Controls whether Native asks one clarification at a time or every currently answerable question in a round.\n# clarification_mode: sequential | batch", + "native.snapshot": "# Controls the auditable project scope and bounded work used by Native content snapshots.", + "native.snapshot.include": "# Selects the project-relative paths included in Native snapshots. Patterns use / and support *, **, and ?.", + "native.snapshot.exclude": "# Removes paths from the included scope. Exclusions are bound into each new change baseline.", + "native.snapshot.max_files": "# Bounds the number of files captured by one snapshot. Increase it for large monorepos.", + "native.snapshot.max_total_bytes": "# Bounds the total file content hashed by one snapshot. Content is streamed and does not depend on Git hashes.", + "native.snapshot.max_duration_ms": "# Bounds snapshot capture time in milliseconds. Increase it together with the byte budget on slower or larger repositories.", + classic: "# Classic workflow settings. They do not change Native state or behavior.", + "classic.language": "# Artifact language used by Classic workflow documents.\n# language: en | zh-CN", + "classic.context_compression": "# Controls beta context compression for new Classic changes.\n# context_compression: off | beta", + "classic.review_mode": "# Sets the default review depth for new Classic changes.\n# review_mode: off | standard | thorough", + "classic.auto_transition": "# Automatically enters the next Classic phase after a phase passes.\n# auto_transition: true | false" + }, + "zh-CN": { + schema: "# Comet 使用的配置格式版本,请勿修改此值。", + default_workflow: "# `/comet` 默认进入的工作流;该值也必须出现在 workflows 中。", + workflows: "# 此项目启用的工作流,可填写 native、classic 或同时启用两者。", + ambient_resume: "# 是否启用只读的环境感知恢复探针,同时作用于 Native 和 Classic;设为 false 可关闭自动工作流恢复。\n# ambient_resume: true | false", + native: "# Native 工作流配置,不会改变 Classic 的状态或行为。", + "native.artifact_root": "# Native 产物的存放根目录,包括规格、change 和运行时数据。", + "native.language": "# Native 工作流文档使用的产物语言。\n# 可选值:en | zh-CN", + "native.clarification_mode": "# Native 每轮询问一个问题,或一次提出当前所有可回答的问题。\n# 可选值:sequential | batch", + "native.snapshot": "# Native 内容快照使用的可审计项目范围与有界工作预算。", + "native.snapshot.include": "# Native 快照纳入的项目相对路径;模式使用 /,支持 *、** 和 ?。", + "native.snapshot.exclude": "# 从纳入范围中排除路径;新 change 会把排除策略绑定到 baseline。", + "native.snapshot.max_files": "# 单次快照最多捕获的文件数;大型 monorepo 可按需提高。", + "native.snapshot.max_total_bytes": "# 单次快照最多哈希的文件内容总字节数;内容采用流式读取,不依赖 Git hash。", + "native.snapshot.max_duration_ms": "# 单次快照的最长执行时间(毫秒);较慢或更大的仓库应与字节预算一并提高。", + classic: "# Classic 工作流配置,不会改变 Native 的状态或行为。", + "classic.language": "# Classic 工作流文档使用的产物语言。\n# 可选值:en | zh-CN", + "classic.context_compression": "# 新建 Classic change 是否启用 beta 上下文压缩。\n# 可选值:off | beta", + "classic.review_mode": "# 新建 Classic change 默认使用的审查深度。\n# 可选值:off | standard | thorough", + "classic.auto_transition": "# Classic 阶段通过后是否自动进入下一阶段。\n# 可选值:true | false" + } +}; +function projectConfigComment(key, language) { + return COMMENTS[language][key]; +} +function commentKey(line, block, nativeNested) { + const match = /^(\s*)([a-z_]+):/u.exec(line); + if (!match) return null; + const indent = match[1].length; + const key = match[2]; + if (indent === 0 && key in COMMENTS.en) return key; + if (indent === 2 && block) { + const blockKey = `${block}.${key}`; + if (blockKey in COMMENTS.en) return blockKey; + } + if (indent === 4 && block === "native" && nativeNested === "snapshot") { + const nestedKey = `native.snapshot.${key}`; + if (nestedKey in COMMENTS.en) return nestedKey; + } + return null; +} +function renderStructuredProjectConfig(value, language) { + const output = []; + let block = null; + let nativeNested = null; + for (const line of (0, import_yaml.stringify)(value).trimEnd().split("\n")) { + const key = commentKey(line, block, nativeNested); + if (key) { + const indent = line.match(/^\s*/u)?.[0] ?? ""; + for (const comment of projectConfigComment(key, language).split("\n")) { + output.push(`${indent}${comment}`); + } + } + output.push(line); + if (/^[a-z_]+:/u.test(line)) { + if (line.startsWith("native:")) block = "native"; + else if (line.startsWith("classic:")) block = "classic"; + else block = null; + nativeNested = null; + } else if (/^ {2}[a-z_]+:/u.test(line) && block === "native") { + nativeNested = line.startsWith(" snapshot:") ? "snapshot" : null; + } + } + output.push(""); + return output.join("\n"); +} + +// domains/comet-native/native-protected-file.ts +import { createHash as createHash3 } from "node:crypto"; +import { constants as fsConstants2, promises as fs4 } from "node:fs"; +import path4 from "node:path"; +import { TextDecoder as TextDecoder2 } from "node:util"; +function isInside3(parent, target) { + const relative = path4.relative(parent, target); + return relative === "" || !path4.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path4.sep}`); +} +function positiveLimit2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity3(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity3(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity2(directory, label) { + const stat = await fs4.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs4.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain2(root, directory, label) { + const lexicalRoot = path4.resolve(root); + const lexicalDirectory = path4.resolve(directory); + if (!isInside3(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity2(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path4.relative(lexicalRoot, lexicalDirectory).split(path4.sep).filter(Boolean)) { + await verifyDirectoryChain3(chain, label); + cursor = path4.join(cursor, segment); + const identity = await captureDirectoryIdentity2(cursor, label); + if (!isInside3(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain3(chain, label); + return chain; +} +async function verifyDirectoryChain3(chain, label) { + for (const identity of chain) { + const stat = await fs4.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity3(identity, stat) || await fs4.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit2(options.maxBytes); + const file = path4.resolve(options.file); + const chain = await captureDirectoryChain2(options.root, path4.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity2(path4.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain3(chain, options.label); + const before = await fs4.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs4.realpath(file); + if (!isInside3(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside3(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants2.O_RDONLY : fsConstants2.O_RDONLY | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK; + const handle = await fs4.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs4.lstat(file), + fs4.realpath(file) + ]); + await verifyDirectoryChain3(chain, options.label); + await verifyDirectoryChain3(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity3(beforeIdentity, opened) || !sameFileIdentity3(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs4.lstat(file), + fs4.realpath(file) + ]); + await verifyDirectoryChain3(chain, options.label); + await verifyDirectoryChain3(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity3(beforeIdentity, afterHandle) || !sameFileIdentity3(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash3("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot2 = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder2("utf-8", { fatal: true }).decode(snapshot2.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot2, text }; +} +async function readNativeProtectedDirectory(options) { + const chain = await captureDirectoryChain2(options.root, options.directory, options.label); + let entries; + if (options.maxEntries === void 0) { + entries = await fs4.readdir(options.directory, { withFileTypes: true }); + } else { + const maxEntries = positiveLimit2(options.maxEntries); + entries = []; + const directory = await fs4.opendir(options.directory); + try { + for await (const entry2 of directory) { + entries.push(entry2); + if (entries.length > maxEntries) { + throw new Error(`${options.label} exceeds ${maxEntries} entries`); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + } + await verifyDirectoryChain3(chain, options.label); + return { + entries, + verify: () => verifyDirectoryChain3(chain, options.label) + }; +} +async function captureNativeProtectedDirectoryGuard(options) { + const chain = await captureDirectoryChain2(options.root, options.directory, options.label); + return { verify: () => verifyDirectoryChain3(chain, options.label) }; +} +async function quarantineNativeProtectedDirectoryInternal(options) { + const directory = path4.resolve(options.directory); + const quarantine = path4.resolve(options.quarantine); + if (path4.dirname(quarantine) !== path4.dirname(directory) || !isInside3(path4.resolve(options.root), quarantine) || quarantine === directory) { + throw new Error(`${options.label} quarantine must be a distinct sibling inside its root`); + } + const parentChain = await captureDirectoryChain2( + options.root, + path4.dirname(directory), + options.label + ); + const identity = await captureDirectoryIdentity2(directory, options.label); + if (!isInside3(parentChain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + await options.beforeQuarantine?.(); + await verifyDirectoryChain3(parentChain, options.label); + const current = await fs4.lstat(directory); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(identity, current) || await fs4.realpath(directory) !== identity.realPath) { + throw new Error(`${options.label} changed before quarantine`); + } + try { + await fs4.lstat(quarantine); + throw new Error(`${options.label} quarantine path is occupied`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await fs4.rename(directory, quarantine); + await verifyDirectoryChain3(parentChain, options.label); + const quarantined = await fs4.lstat(quarantine); + if (!quarantined.isDirectory() || quarantined.isSymbolicLink() || !sameDirectoryIdentity3(identity, quarantined)) { + throw new Error(`${options.label} changed while quarantining`); + } + await options.afterQuarantine?.(quarantine); + try { + await fs4.lstat(directory); + throw new Error(`${options.label} path was recreated while quarantining`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + return { quarantine, parentChain, identity }; +} +async function quarantineNativeProtectedDirectory(options) { + await quarantineNativeProtectedDirectoryInternal(options); +} +async function removeNativeProtectedFile(options) { + const file = path4.resolve(options.file); + const parentChain = await captureDirectoryChain2(options.root, path4.dirname(file), options.label); + const before = await fs4.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + const identity = asFileIdentity(before); + const realPath = await fs4.realpath(file); + if (!isInside3(parentChain[0].realPath, realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + const snapshot2 = await readNativeProtectedFile({ + root: options.root, + file, + maxBytes: options.maxBytes, + label: options.label + }); + if (snapshot2.hash !== options.expectedHash || snapshot2.size !== options.expectedSize) { + throw new Error(`${options.label} changed before removal`); + } + const [afterRead, afterReadRealPath] = await Promise.all([fs4.lstat(file), fs4.realpath(file)]); + if (!afterRead.isFile() || afterRead.isSymbolicLink() || !sameFileIdentity3(identity, afterRead) || afterReadRealPath !== realPath) { + throw new Error(`${options.label} changed while verifying removal`); + } + await options.beforeRemove?.(); + await verifyDirectoryChain3(parentChain, options.label); + const [current, currentRealPath] = await Promise.all([fs4.lstat(file), fs4.realpath(file)]); + if (!current.isFile() || current.isSymbolicLink() || !sameFileIdentity3(identity, current) || currentRealPath !== realPath) { + throw new Error(`${options.label} changed before removal`); + } + await fs4.rm(file); + await verifyDirectoryChain3(parentChain, options.label); +} +async function removeNativeProtectedEmptyDirectory(options) { + const directory = path4.resolve(options.directory); + const parentChain = await captureDirectoryChain2( + options.root, + path4.dirname(directory), + options.label + ); + const identity = await captureDirectoryIdentity2(directory, options.label); + if (!isInside3(parentChain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + await options.beforeRemove?.(); + await verifyDirectoryChain3(parentChain, options.label); + const current = await fs4.lstat(directory); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(identity, current) || await fs4.realpath(directory) !== identity.realPath) { + throw new Error(`${options.label} changed before removal`); + } + await fs4.rmdir(directory); + await verifyDirectoryChain3(parentChain, options.label); +} +async function ensureNativeProtectedDirectory(options) { + const lexicalRoot = path4.resolve(options.root); + const lexicalDirectory = path4.resolve(options.directory); + if (!isInside3(lexicalRoot, lexicalDirectory)) { + throw new Error(`${options.label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity2(lexicalRoot, options.label)]; + let cursor = lexicalRoot; + for (const segment of path4.relative(lexicalRoot, lexicalDirectory).split(path4.sep).filter(Boolean)) { + await verifyDirectoryChain3(chain, options.label); + cursor = path4.join(cursor, segment); + try { + await fs4.mkdir(cursor); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const identity = await captureDirectoryIdentity2(cursor, options.label); + if (!isInside3(chain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain3(chain, options.label); +} +async function moveNativeProtectedDirectory(options) { + const root = path4.resolve(options.root); + const source = path4.resolve(options.source); + const target = path4.resolve(options.target); + if (source === target || !isInside3(root, source) || !isInside3(root, target) || isInside3(source, target) || isInside3(target, source)) { + throw new Error(`${options.label} source and target must be distinct paths inside one root`); + } + await ensureNativeProtectedDirectory({ + root, + directory: path4.dirname(target), + label: `${options.label} target parent` + }); + const sourceParentChain = await captureDirectoryChain2(root, path4.dirname(source), options.label); + const targetParentChain = await captureDirectoryChain2(root, path4.dirname(target), options.label); + const sourceIdentity = await captureDirectoryIdentity2(source, options.label); + if (!isInside3(sourceParentChain[0].realPath, sourceIdentity.realPath)) { + throw new Error(`${options.label} source resolves outside its managed root`); + } + try { + await fs4.lstat(target); + throw new Error(`${options.label} target already exists`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await options.beforeMove?.(); + await Promise.all([ + verifyDirectoryChain3(sourceParentChain, options.label), + verifyDirectoryChain3(targetParentChain, options.label) + ]); + const current = await fs4.lstat(source); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(sourceIdentity, current) || await fs4.realpath(source) !== sourceIdentity.realPath) { + throw new Error(`${options.label} source changed before move`); + } + await fs4.rename(source, target); + await Promise.all([ + verifyDirectoryChain3(sourceParentChain, options.label), + verifyDirectoryChain3(targetParentChain, options.label) + ]); + const moved = await fs4.lstat(target); + if (!moved.isDirectory() || moved.isSymbolicLink() || !sameDirectoryIdentity3(sourceIdentity, moved) || !isInside3(sourceParentChain[0].realPath, await fs4.realpath(target))) { + throw new Error(`${options.label} source identity changed while moving`); + } + try { + await fs4.lstat(source); + throw new Error(`${options.label} source was recreated while moving`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } +} +async function copyNativeProtectedFile(options) { + const source = await readNativeProtectedFile({ + root: options.sourceRoot, + file: options.source, + maxBytes: options.maxBytes, + label: options.label, + hooks: options.hooks, + forbiddenRoots: options.forbiddenRoots + }); + if (options.expectedHash !== void 0 && source.hash !== options.expectedHash) { + throw new Error(`${options.label} content changed before copy`); + } + await atomicWriteBytes(options.target, source.bytes, { + containedRoot: options.targetRoot, + exclusive: options.exclusive, + beforeCommit: async () => { + await options.hooks?.beforeTargetCommit?.(); + if (options.expectedTargetHash === void 0) return; + if (options.expectedTargetHash === null) { + try { + await fs4.lstat(options.target); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + throw new Error(`${options.label} target changed before commit`); + } + const target = await readNativeProtectedFile({ + root: options.targetRoot, + file: options.target, + maxBytes: options.maxBytes, + label: `${options.label} existing target` + }); + if (target.hash !== options.expectedTargetHash) { + throw new Error(`${options.label} target changed before commit`); + } + } + }); + const persisted = await readNativeProtectedFile({ + root: options.targetRoot, + file: options.target, + maxBytes: options.maxBytes, + label: `${options.label} target` + }); + if (persisted.hash !== source.hash || persisted.size !== source.size) { + throw new Error(`${options.label} target could not be verified`); + } + return persisted; +} + +// domains/comet-native/native-paths.ts +import { promises as fs5 } from "fs"; +import path5 from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs5.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs5.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +function inside(parent, target) { + const relative = path5.relative(parent, target); + return relative === "" || !path5.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path5.sep}`); +} +async function physicalPath(target) { + const missing = []; + let cursor = target; + while (!await isFileOrDirectory(cursor)) { + const parent = path5.dirname(cursor); + if (parent === cursor) break; + missing.push(path5.basename(cursor)); + cursor = parent; + } + const existing = await fs5.realpath(cursor); + return path5.resolve(existing, ...missing.reverse()); +} +async function isSymbolicLink(target) { + try { + return (await fs5.lstat(target)).isSymbolicLink(); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path5.resolve(startPath); + try { + if (!(await fs5.stat(cursor)).isDirectory()) cursor = path5.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path5.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path5.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path5.join(cursor, ".git"))) return cursor; + const parent = path5.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path5.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized = path5.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized === ".." || normalized.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized === "" ? "." : normalized; +} +async function resolveArtifactRoot(projectRoot, value) { + const normalized = normalizeArtifactRootRef(value); + const lexical = path5.resolve(projectRoot, ...normalized.split("/")); + const physicalProject = await fs5.realpath(projectRoot); + const physicalTarget = await physicalPath(lexical); + if (!inside(physicalProject, physicalTarget)) { + throw new Error("native.artifact_root resolves outside the project root"); + } + return lexical; +} +async function nativeProjectPaths(projectRoot, artifactRootRef) { + const normalized = normalizeArtifactRootRef(artifactRootRef); + const artifactRoot = await resolveArtifactRoot(projectRoot, normalized); + const nativeRoot = path5.join(artifactRoot, "comet"); + if (await isSymbolicLink(nativeRoot)) { + throw new Error("The configured Native comet root must not be a symbolic link"); + } + const [physicalArtifactRoot, physicalNativeRoot] = await Promise.all([ + physicalPath(artifactRoot), + physicalPath(nativeRoot) + ]); + if (!inside(physicalArtifactRoot, physicalNativeRoot)) { + throw new Error("The configured Native comet root resolves outside its artifact root"); + } + return { + projectRoot: path5.resolve(projectRoot), + configFile: path5.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")), + artifactRoot, + artifactRootRef: normalized, + nativeRoot, + specsDir: path5.join(nativeRoot, "specs"), + changesDir: path5.join(nativeRoot, "changes"), + archiveDir: path5.join(nativeRoot, "archive"), + runtimeDir: path5.join(nativeRoot, "runtime"), + locksDir: path5.join(nativeRoot, "runtime", "locks"), + transactionsDir: path5.join(nativeRoot, "runtime", "transactions") + }; +} +async function ensureNativeDirectories(paths) { + const directories = [ + paths.specsDir, + paths.changesDir, + paths.archiveDir, + paths.locksDir, + paths.transactionsDir + ]; + await Promise.all( + directories.map(async (directory) => { + await resolveContainedNativePath(paths.nativeRoot, directory); + await fs5.mkdir(directory, { recursive: true }); + }) + ); +} +function isInsidePath(parent, target) { + return inside(path5.resolve(parent), path5.resolve(target)); +} +async function resolveContainedNativePath(root, target) { + const lexicalRoot = path5.resolve(root); + const lexicalTarget = path5.resolve(target); + if (!inside(lexicalRoot, lexicalTarget)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + if (await isSymbolicLink(lexicalRoot)) { + throw new Error(`Native root must not be a symbolic link: ${root}`); + } + const [physicalRoot, physicalTarget] = await Promise.all([ + physicalPath(lexicalRoot), + physicalPath(lexicalTarget) + ]); + if (!inside(physicalRoot, physicalTarget)) { + throw new Error(`Path resolves outside the Native root: ${target}`); + } + return lexicalTarget; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot2 = record(value, "native.snapshot"); + rejectUnknown(snapshot2, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot2.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot2.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot2.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot2.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot2.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot2 = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot: snapshot2, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +function defaultProjectConfig(artifactRoot = "docs", language = "en") { + return { + schema: "comet.project.v1", + default_workflow: "native", + ambient_resume: true, + native: { + artifact_root: normalizeArtifactRootRef(artifactRoot), + language, + clarification_mode: "sequential", + snapshot: { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] } + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs6.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} +async function existingConfigDocument(projectRoot) { + const file = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + try { + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) throw new Error(document.errors[0].message); + const value = document.toJS(); + if (value && typeof value === "object" && !Array.isArray(value)) { + return value; + } + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + return {}; +} +async function assertNoPendingNativeRootMove(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (config?.native.pending_root_move) { + throw new Error( + `Native root move ${config.native.pending_root_move.id} is incomplete; use comet native doctor --repair` + ); + } +} +async function writeProjectConfig(projectRoot, config) { + const validated = parseConfig({ + schema: config.schema, + default_workflow: config.default_workflow, + workflows: config.workflows ?? [config.default_workflow], + ambient_resume: config.ambient_resume, + native: { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot, + ...config.native.pending_root_move ? { + pending_root_move: { + id: config.native.pending_root_move.id, + from_artifact_root: config.native.pending_root_move.fromArtifactRoot, + to_artifact_root: config.native.pending_root_move.toArtifactRoot, + stage: config.native.pending_root_move.stage, + ...config.native.pending_root_move.cleanup ? { + cleanup: { + kind: config.native.pending_root_move.cleanup.kind, + state: config.native.pending_root_move.cleanup.state, + manifest_hash: config.native.pending_root_move.cleanup.manifestHash + } + } : {} + } + } : {} + } + }); + const document = { + ...await existingConfigDocument(projectRoot), + schema: validated.schema, + default_workflow: validated.default_workflow, + workflows: validated.workflows, + ambient_resume: validated.ambient_resume, + native: { + artifact_root: validated.native.artifact_root, + language: validated.native.language, + clarification_mode: validated.native.clarification_mode, + snapshot: validated.native.snapshot, + ...validated.native.pending_root_move ? { + pending_root_move: { + id: validated.native.pending_root_move.id, + from_artifact_root: validated.native.pending_root_move.fromArtifactRoot, + to_artifact_root: validated.native.pending_root_move.toArtifactRoot, + stage: validated.native.pending_root_move.stage, + ...validated.native.pending_root_move.cleanup ? { + cleanup: { + kind: validated.native.pending_root_move.cleanup.kind, + state: validated.native.pending_root_move.cleanup.state, + manifest_hash: validated.native.pending_root_move.cleanup.manifestHash + } + } : {} + } + } : {} + } + }; + const canonical = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + await fs6.mkdir(path6.dirname(canonical), { recursive: true }); + await atomicWriteText( + canonical, + renderStructuredProjectConfig(document, validated.native.language === "zh-CN" ? "zh-CN" : "en") + ); +} +async function resolveNativeProject(options) { + const projectRoot = await discoverNativeProject(options.startPath); + const existing = await readProjectConfig(projectRoot); + if (!existing && options.allowMissingConfig === false) { + throw new Error(`${PROJECT_CONFIG_FILE} was not found`); + } + if (existing?.native.pending_root_move) { + throw new Error( + `Native root move ${existing.native.pending_root_move.id} is incomplete; use comet native doctor --repair` + ); + } + const explicit = options.explicitArtifactRoot ? normalizeArtifactRootRef(options.explicitArtifactRoot) : void 0; + if (existing && explicit && explicit !== existing.native.artifact_root) { + throw new Error( + `Configured Native artifact root is ${existing.native.artifact_root}; refusing conflicting root ${explicit}` + ); + } + const config = existing ?? defaultProjectConfig(explicit ?? "docs"); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + return { config, paths, configured: existing !== null }; +} + +// domains/comet-native/native-mutation-lock.ts +import { promises as fs9 } from "fs"; +import path9 from "path"; + +// domains/comet-native/native-lock.ts +import { AsyncLocalStorage } from "async_hooks"; +import { randomUUID as randomUUID2 } from "crypto"; +import { promises as fs7 } from "fs"; +import os2 from "os"; +import path7 from "path"; +var NATIVE_LOCK_MAX_BYTES = 16 * 1024; +var NATIVE_LOCK_COORDINATOR_DIR = ".coordinator"; +var NATIVE_LOCK_COORDINATOR_TIMEOUT_MS = 5e3; +var nativeLockCoordinator = new AsyncLocalStorage(); +var nativeLockLocalCoordinator = /* @__PURE__ */ new Map(); +function lockName(value) { + if (!/^[a-z][a-z0-9-]*$/u.test(value)) throw new Error(`Invalid Native lock name: ${value}`); + return `${value}.lock`; +} +function parseNativeLockOwner(value, file) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Invalid Native lock metadata: ${file}`); + } + const owner = value; + if (typeof owner.id !== "string" || owner.id.length === 0 || typeof owner.pid !== "number" || !Number.isSafeInteger(owner.pid) || owner.pid < 1 || typeof owner.hostname !== "string" || owner.hostname.length === 0 || typeof owner.createdAt !== "string" || owner.createdAt.length === 0 || typeof owner.operation !== "string" || owner.operation.length === 0) { + throw new Error(`Invalid Native lock metadata: ${file}`); + } + return owner; +} +function nativeLockFileIdentity(stat) { + return { + device: stat.dev.toString(), + inode: stat.ino.toString(), + size: stat.size.toString(), + birthtimeNs: stat.birthtimeNs.toString(), + ctimeNs: stat.ctimeNs.toString(), + mtimeNs: stat.mtimeNs.toString() + }; +} +function sameNativeLockObject(left, right) { + const leftObject = { dev: left.device, ino: left.inode, birthtime: left.birthtimeNs }; + const rightObject = { dev: right.device, ino: right.inode, birthtime: right.birthtimeNs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.size === right.size; +} +function sameNativeLockVersion(left, right) { + return sameNativeLockObject(left, right) && left.size === right.size && left.birthtimeNs === right.birthtimeNs && left.ctimeNs === right.ctimeNs && left.mtimeNs === right.mtimeNs; +} +function sameNativeLockDiagnosis(left, right) { + if (left.status !== right.status) return false; + if (!left.owner || !left.identity || !right.owner || !right.identity) { + return left.owner === right.owner && left.identity === right.identity; + } + return left.owner.id === right.owner.id && sameNativeLockVersion(left.identity, right.identity); +} +async function readNativeLockSnapshot(file) { + let bytes; + let stat; + try { + const result2 = await readFileRaceSafe(file, NATIVE_LOCK_MAX_BYTES, { + bigint: true, + label: "Native lock" + }); + bytes = result2.bytes; + stat = result2.stat; + } catch (error) { + if (error.code === "ENOENT") return null; + if (error instanceof RaceSafeReadError) { + if (error.reason === "too-large") { + throw new Error(`Native lock metadata exceeds ${NATIVE_LOCK_MAX_BYTES} bytes: ${file}`, { + cause: error + }); + } + if (error.reason === "not-regular-file") { + throw new Error(`Native lock must be a regular file: ${file}`, { cause: error }); + } + throw new Error(`Native lock changed while reading: ${file}`, { cause: error }); + } + throw error; + } + return { + file, + owner: parseNativeLockOwner(JSON.parse(bytes.toString("utf8")), file), + identity: nativeLockFileIdentity(stat) + }; +} +async function readNativeLock(file) { + return (await readNativeLockSnapshot(file))?.owner ?? null; +} +function diagnosisFromSnapshot(snapshot2) { + if (!snapshot2) return { status: "missing", owner: null, identity: null }; + if (snapshot2.owner.hostname !== os2.hostname()) { + return { status: "unknown", owner: snapshot2.owner, identity: snapshot2.identity }; + } + const alive = isProcessAlive(snapshot2.owner.pid); + return { + status: alive === true ? "active" : alive === false ? "stale" : "unknown", + owner: snapshot2.owner, + identity: snapshot2.identity + }; +} +async function restoreQuarantinedNativeLock(quarantine, file) { + try { + await fs7.lstat(file); + return; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + try { + await fs7.rename(quarantine, file); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } +} +async function removeBoundNativeLock(expected, quarantineDir) { + const current = await readNativeLockSnapshot(expected.file); + if (!current) return "missing"; + if (current.owner.id !== expected.owner.id) { + throw new Error(`Native lock ownership changed: ${expected.file}`); + } + if (!sameNativeLockVersion(current.identity, expected.identity)) { + throw new Error(`Native lock identity changed: ${expected.file}`); + } + await fs7.mkdir(quarantineDir, { recursive: true }); + const quarantine = path7.join( + quarantineDir, + `${path7.basename(expected.file)}.${expected.owner.id}.${randomUUID2()}.removed` + ); + try { + await fs7.rename(expected.file, quarantine); + } catch (error) { + if (error.code === "ENOENT") return "missing"; + throw error; + } + const moved = await readNativeLockSnapshot(quarantine); + if (!moved || moved.owner.id !== expected.owner.id || !sameNativeLockObject(moved.identity, expected.identity)) { + await restoreQuarantinedNativeLock(quarantine, expected.file); + throw new Error(`Native lock changed before quarantine: ${expected.file}`); + } + await fs7.rm(quarantine, { force: true }); + return "removed"; +} +function newNativeLockOwner(operation) { + return { + id: randomUUID2(), + pid: process.pid, + hostname: os2.hostname(), + createdAt: (/* @__PURE__ */ new Date()).toISOString(), + operation + }; +} +async function writeNativeLockFile(file, owner) { + let handle; + try { + handle = await fs7.open(file, "wx"); + } catch (error) { + if (error.code === "EEXIST") { + const existing = await readNativeLock(file); + throw new Error( + `Native lock is already held: ${file}${existing ? ` by pid ${existing.pid} for ${existing.operation}` : ""}`, + { cause: error } + ); + } + throw error; + } + try { + await handle.writeFile(JSON.stringify(owner, null, 2) + "\n", "utf8"); + await handle.sync(); + return nativeLockFileIdentity(await handle.stat({ bigint: true })); + } finally { + await handle.close(); + } +} +async function publishNativeCoordinatorClaim(paths, operation) { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + await fs7.mkdir(locksDir, { recursive: true }); + const coordinatorDir = await resolveContainedNativePath( + paths.nativeRoot, + path7.join(locksDir, NATIVE_LOCK_COORDINATOR_DIR) + ); + await fs7.mkdir(coordinatorDir, { recursive: true }); + const owner = newNativeLockOwner(operation); + const temporary = path7.join(coordinatorDir, `.${owner.id}.tmp`); + const file = path7.join(coordinatorDir, `${owner.id}.claim`); + try { + const identity = await writeNativeLockFile(temporary, owner); + await fs7.rename(temporary, file); + const published = await readNativeLockSnapshot(file); + if (!published || !sameNativeLockObject(identity, published.identity)) { + throw new Error(`Native lock coordinator claim changed while publishing: ${file}`); + } + return { file, nativeRoot: paths.nativeRoot, locksDir, owner, identity: published.identity }; + } finally { + await fs7.rm(temporary, { force: true }); + } +} +async function hasNativeCoordinatorPredecessor(claim) { + const coordinatorDir = path7.dirname(claim.file); + let predecessor = false; + const claimName = path7.basename(claim.file); + for (const entry2 of await fs7.readdir(coordinatorDir, { withFileTypes: true })) { + if (!entry2.isFile() || entry2.isSymbolicLink() || !entry2.name.endsWith(".claim")) continue; + const file = path7.join(coordinatorDir, entry2.name); + if (path7.resolve(file) === path7.resolve(claim.file)) continue; + try { + const snapshot2 = await readNativeLockSnapshot(file); + const diagnosis = diagnosisFromSnapshot(snapshot2); + if (diagnosis.status === "missing") continue; + if (diagnosis.status === "stale" && snapshot2) { + await removeBoundNativeLock(snapshot2, coordinatorDir); + continue; + } + if (entry2.name < claimName) predecessor = true; + } catch { + if (entry2.name < claimName) predecessor = true; + } + } + return predecessor; +} +async function releaseNativeCoordinatorClaim(claim) { + const current = await readNativeLockSnapshot(claim.file); + if (!current) return; + if (current.owner.id !== claim.owner.id || !sameNativeLockVersion(current.identity, claim.identity)) { + throw new Error(`Native lock coordinator ownership changed: ${claim.file}`); + } + await removeBoundNativeLock(current, path7.dirname(claim.file)); +} +async function acquireNativeCoordinator(paths, operation) { + const deadline = Date.now() + NATIVE_LOCK_COORDINATOR_TIMEOUT_MS; + while (true) { + const claim = await publishNativeCoordinatorClaim(paths, operation); + if (!await hasNativeCoordinatorPredecessor(claim)) return claim; + await releaseNativeCoordinatorClaim(claim); + if (Date.now() >= deadline) { + throw new Error(`Native lock coordinator is busy: ${paths.locksDir}`); + } + await new Promise((resolve) => setTimeout(resolve, 2 + Math.floor(Math.random() * 7))); + } +} +async function acquireNativeLocalCoordinator(key) { + const previous = nativeLockLocalCoordinator.get(key) ?? Promise.resolve(); + let release; + const current = new Promise((resolve) => { + release = resolve; + }); + const turn = previous.then(() => current); + nativeLockLocalCoordinator.set(key, turn); + await previous; + return () => { + release(); + if (nativeLockLocalCoordinator.get(key) === turn) nativeLockLocalCoordinator.delete(key); + }; +} +async function withNativeLockCoordinator(paths, operation, work) { + const key = path7.resolve(paths.locksDir); + const current = nativeLockCoordinator.getStore(); + if (current?.has(key)) return work(); + const releaseLocal = await acquireNativeLocalCoordinator(key); + try { + const claim = await acquireNativeCoordinator(paths, operation); + const next = new Map(current ?? []); + next.set(key, claim); + return await nativeLockCoordinator.run(next, async () => { + try { + return await work(); + } finally { + await releaseNativeCoordinatorClaim(claim); + } + }); + } finally { + releaseLocal(); + } +} +async function withNativeLockRecovery(pathEntries, operation, work) { + const unique = [ + ...new Map(pathEntries.map((entry2) => [path7.resolve(entry2.locksDir), entry2])).values() + ].sort((left, right) => path7.resolve(left.locksDir).localeCompare(path7.resolve(right.locksDir))); + const enter = async (index) => { + const entry2 = unique[index]; + return entry2 ? withNativeLockCoordinator(entry2, operation, () => enter(index + 1)) : work(); + }; + return enter(0); +} +async function acquireNativeLock(paths, name, operation) { + return withNativeLockCoordinator(paths, `acquire ${name}`, async () => { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + await fs7.mkdir(locksDir, { recursive: true }); + const file = await resolveContainedNativePath( + paths.nativeRoot, + path7.join(locksDir, lockName(name)) + ); + const owner = newNativeLockOwner(operation); + const identity = await writeNativeLockFile(file, owner); + return { file, nativeRoot: paths.nativeRoot, locksDir, owner, identity }; + }); +} +async function releaseNativeLock(lock) { + if (!await readNativeLockSnapshot(lock.file)) return; + await withNativeLockCoordinator(lock, `release ${path7.basename(lock.file)}`, async () => { + const current = await readNativeLockSnapshot(lock.file); + if (!current) return; + if (current.owner.id !== lock.owner.id) { + throw new Error(`Native lock ownership changed: ${lock.file}`); + } + if (!sameNativeLockVersion(current.identity, lock.identity)) { + throw new Error(`Native lock identity changed: ${lock.file}`); + } + const coordinatorDir = path7.join(lock.locksDir, NATIVE_LOCK_COORDINATOR_DIR); + await removeBoundNativeLock(current, coordinatorDir); + }); +} +function isProcessAlive(pid) { + try { + process.kill(pid, 0); + return true; + } catch (error) { + const code = error.code; + if (code === "ESRCH") return false; + if (code === "EPERM") return true; + return null; + } +} +async function diagnoseNativeLock(file) { + return diagnosisFromSnapshot(await readNativeLockSnapshot(file)); +} +async function takeOverNativeStaleLock(paths, file, expected) { + return withNativeLockCoordinator(paths, `take over ${path7.basename(file)}`, async () => { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + const containedFile = await resolveContainedNativePath(paths.nativeRoot, file); + if (path7.resolve(path7.dirname(containedFile)) !== path7.resolve(locksDir)) { + throw new Error(`Native lock takeover target is outside the lock directory: ${file}`); + } + const snapshot2 = await readNativeLockSnapshot(containedFile); + const diagnosis = diagnosisFromSnapshot(snapshot2); + if (diagnosis.status === "missing") return { status: "missing" }; + if (expected && !sameNativeLockDiagnosis(expected, diagnosis)) { + return { status: "changed", diagnosis }; + } + if (diagnosis.status !== "stale" || !snapshot2) { + return { status: "changed", diagnosis }; + } + const coordinatorDir = path7.join(locksDir, NATIVE_LOCK_COORDINATOR_DIR); + const removed = await removeBoundNativeLock(snapshot2, coordinatorDir); + return removed === "removed" ? { status: "removed", owner: snapshot2.owner } : { status: "missing" }; + }); +} + +// domains/comet-native/native-transaction.ts +import { promises as fs8 } from "fs"; +import path8 from "path"; +import { TextDecoder as TextDecoder3 } from "util"; +var JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "kind", + "status", + "projectRoot", + "nativeRoot", + "change", + "createdAt", + "operations" +]); +var OPERATION_KEYS = /* @__PURE__ */ new Set(["id", "type", "source", "target", "staged", "backup"]); +var ARCHIVE_V2_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "kind", + "status", + "change", + "createdAt", + "preflightHash", + "operations" +]); +var ARCHIVE_V2_OPERATION_KEYS = /* @__PURE__ */ new Set([ + "id", + "type", + "source", + "target", + "staged", + "backup", + "expectedSourceHash", + "expectedTargetHash", + "stagedHash" +]); +var EVENT_KEYS = /* @__PURE__ */ new Set(["sequence", "timestamp", "type", "operationId"]); +var TRANSACTION_STATUSES = /* @__PURE__ */ new Set([ + "prepared", + "applying", + "committed", + "rolling-back", + "rolled-back" +]); +var EVENT_TYPES = /* @__PURE__ */ new Set([ + "prepared", + "operation-started", + "operation-completed", + "archive-finalization-started", + "archive-finalized", + "commit", + "rollback-started", + "rollback-completed" +]); +var NATIVE_TRANSACTION_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_TRANSACTION_EVENTS_MAX_BYTES = 1024 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_BYTES = 16 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_COUNT = 1024; +var NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES = 64 * 1024 * 1024; +var NATIVE_LEGACY_TRANSACTION_DIRECTORY_MAX_ENTRIES = 2e4; +var UTF8_DECODER = new TextDecoder3("utf-8", { fatal: true }); +function record2(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function rejectUnknown2(value, keys, label) { + const unknown = Object.keys(value).filter((key) => !keys.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function validTimestamp(value) { + if (typeof value !== "string") return false; + const parsed = new Date(value); + return !Number.isNaN(parsed.valueOf()) && parsed.toISOString() === value; +} +function assertRef(ref, label) { + if (typeof ref !== "string" || ref.length === 0 || path8.isAbsolute(ref) || /^(?:[A-Za-z]:|~|[\\/])/u.test(ref) || ref.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native root`); + } +} +function assertHash(value, label) { + if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } +} +function assertArchiveRef(ref, label) { + assertRef(ref, label); + if (ref.includes("\\") || ref !== path8.posix.normalize(ref) || ref.split("/").includes(".") || ref.endsWith("/") || Buffer.byteLength(ref, "utf8") > 1024) { + throw new Error(`${label} must be a normalized Native-relative ref`); + } +} +function parseOperation(value, index) { + const operation = record2(value, `transaction operations[${index}]`); + rejectUnknown2(operation, OPERATION_KEYS, `transaction operations[${index}]`); + if (typeof operation.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(operation.id)) { + throw new Error(`transaction operations[${index}].id is invalid`); + } + if (operation.type !== "write" && operation.type !== "remove" && operation.type !== "move") { + throw new Error(`transaction operation ${operation.id} has an invalid type`); + } + assertRef(operation.target, `transaction operation ${operation.id} target`); + for (const field2 of ["source", "staged", "backup"]) { + if (operation[field2] !== void 0) { + assertRef(operation[field2], `transaction operation ${operation.id} ${field2}`); + } + } + if (operation.type === "write") { + if (operation.staged === void 0 || operation.source !== void 0) { + throw new Error(`write operation ${operation.id} requires staged and forbids source`); + } + } else if (operation.type === "remove") { + if (operation.source !== void 0 || operation.staged !== void 0) { + throw new Error(`remove operation ${operation.id} forbids source and staged`); + } + } else if (operation.source === void 0 || operation.staged !== void 0 || operation.backup !== void 0) { + throw new Error(`move operation ${operation.id} requires source and forbids staged and backup`); + } + return operation; +} +function parseNativeArchiveTransactionJournalV2(value) { + const journal = record2(value, "Native Archive transaction journal"); + rejectUnknown2(journal, ARCHIVE_V2_JOURNAL_KEYS, "Native Archive transaction journal"); + if (journal.schema !== "comet.native.transaction.v2") { + throw new Error("Unsupported Native Archive transaction schema"); + } + if (typeof journal.id !== "string" || !/^[a-f0-9-]{8,}$/u.test(journal.id)) { + throw new Error("Native Archive transaction id is invalid"); + } + if (journal.kind !== "archive") throw new Error("Native v2 transaction kind must be archive"); + if (typeof journal.status !== "string" || !TRANSACTION_STATUSES.has(journal.status)) { + throw new Error("Native Archive transaction status is invalid"); + } + if (typeof journal.change !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(journal.change)) { + throw new Error("Native Archive transaction change name is invalid"); + } + if (!validTimestamp(journal.createdAt)) { + throw new Error("Native Archive transaction createdAt is invalid"); + } + assertHash(journal.preflightHash, "Native Archive transaction preflightHash"); + if (!Array.isArray(journal.operations) || journal.operations.length > 65) { + throw new Error("Native Archive transaction operations must be an array"); + } + const operations = journal.operations.map((value2, index) => { + const operation = record2(value2, `Archive transaction operations[${index}]`); + rejectUnknown2(operation, ARCHIVE_V2_OPERATION_KEYS, `Archive transaction operations[${index}]`); + if (typeof operation.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(operation.id)) { + throw new Error(`Archive transaction operations[${index}].id is invalid`); + } + if (operation.type !== "write" && operation.type !== "remove" && operation.type !== "move") { + throw new Error(`Archive transaction operation ${operation.id} has an invalid type`); + } + assertArchiveRef(operation.target, `Archive transaction operation ${operation.id} target`); + for (const field2 of ["source", "staged", "backup"]) { + if (operation[field2] !== void 0) { + assertArchiveRef( + operation[field2], + `Archive transaction operation ${operation.id} ${field2}` + ); + } + } + if (operation.expectedTargetHash !== null) { + assertHash( + operation.expectedTargetHash, + `Archive transaction operation ${operation.id} expectedTargetHash` + ); + } + if (operation.type === "write") { + if (operation.staged === void 0 || operation.source !== void 0 || operation.expectedSourceHash !== void 0) { + throw new Error( + `Archive write operation ${operation.id} requires staged and forbids source` + ); + } + assertHash(operation.stagedHash, `Archive write operation ${operation.id} stagedHash`); + if (operation.expectedTargetHash === null !== (operation.backup === void 0)) { + throw new Error( + `Archive write operation ${operation.id} backup must match target existence` + ); + } + } else if (operation.type === "remove") { + if (operation.source !== void 0 || operation.staged !== void 0 || operation.stagedHash !== void 0 || operation.expectedSourceHash !== void 0 || operation.backup === void 0 || operation.expectedTargetHash === null) { + throw new Error( + `Archive remove operation ${operation.id} requires a bound target and backup` + ); + } + } else { + if (operation.source === void 0 || operation.staged !== void 0 || operation.stagedHash !== void 0 || operation.backup !== void 0 || operation.expectedTargetHash !== null) { + throw new Error( + `Archive move operation ${operation.id} requires source and an absent target` + ); + } + assertHash( + operation.expectedSourceHash, + `Archive move operation ${operation.id} expectedSourceHash` + ); + } + return operation; + }); + const operationIds = operations.map((operation) => operation.id); + if (new Set(operationIds).size !== operationIds.length) { + throw new Error("Native Archive transaction operation ids must be unique"); + } + const transactionPrefix = `runtime/transactions/${journal.id}`; + const archiveMoves = operations.filter((operation) => operation.type === "move"); + if (archiveMoves.length !== 1 || archiveMoves[0].id !== "archive-change" || archiveMoves[0].source !== `changes/${journal.change}` || !new RegExp(`^archive/\\d{4}-\\d{2}-\\d{2}-${journal.change}$`, "u").test( + archiveMoves[0].target + ) || operations.at(-1) !== archiveMoves[0]) { + throw new Error("Native Archive transaction must end with its exact change move"); + } + const specTargets = /* @__PURE__ */ new Set(); + for (const operation of operations.slice(0, -1)) { + if (operation.type === "move" || !/^specs\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/spec\.md$/u.test(operation.target) || specTargets.has(operation.target)) { + throw new Error(`Native Archive transaction spec target is invalid: ${operation.target}`); + } + specTargets.add(operation.target); + if (operation.staged !== void 0 && !operation.staged.startsWith(`${transactionPrefix}/staged/specs/`)) { + throw new Error(`Native Archive transaction staged ref is invalid: ${operation.staged}`); + } + if (operation.backup !== void 0 && !operation.backup.startsWith(`${transactionPrefix}/backups/specs/`)) { + throw new Error(`Native Archive transaction backup ref is invalid: ${operation.backup}`); + } + } + return { + schema: "comet.native.transaction.v2", + id: journal.id, + kind: "archive", + status: journal.status, + change: journal.change, + createdAt: journal.createdAt, + preflightHash: journal.preflightHash, + operations + }; +} +function parseJournal(value) { + const journal = record2(value, "Native transaction journal"); + if (journal.schema === "comet.native.transaction.v2") { + return parseNativeArchiveTransactionJournalV2(journal); + } + rejectUnknown2(journal, JOURNAL_KEYS, "Native transaction journal"); + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Unsupported Native transaction schema"); + } + if (typeof journal.id !== "string" || !/^[a-f0-9-]{8,}$/u.test(journal.id)) { + throw new Error("Native transaction id is invalid"); + } + if (journal.kind !== "archive" && journal.kind !== "root-move") { + throw new Error("Native transaction kind is invalid"); + } + if (typeof journal.status !== "string" || !TRANSACTION_STATUSES.has(journal.status)) { + throw new Error("Native transaction status is invalid"); + } + if (typeof journal.projectRoot !== "string" || !path8.isAbsolute(journal.projectRoot) || typeof journal.nativeRoot !== "string" || !path8.isAbsolute(journal.nativeRoot)) { + throw new Error("Native transaction roots must be absolute paths"); + } + if (journal.change !== void 0 && (typeof journal.change !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(journal.change))) { + throw new Error("Native transaction change name is invalid"); + } + if (!validTimestamp(journal.createdAt)) { + throw new Error("Native transaction createdAt is invalid"); + } + if (!Array.isArray(journal.operations)) { + throw new Error("Native transaction operations must be an array"); + } + const operations = journal.operations.map(parseOperation); + const operationIds = operations.map((operation) => operation.id); + if (new Set(operationIds).size !== operationIds.length) { + throw new Error("Native transaction operation ids must be unique"); + } + return { + schema: "comet.native.transaction.v1", + id: journal.id, + kind: journal.kind, + status: journal.status, + projectRoot: journal.projectRoot, + nativeRoot: journal.nativeRoot, + ...typeof journal.change === "string" ? { change: journal.change } : {}, + createdAt: journal.createdAt, + operations + }; +} +function parseEvent(value, line) { + const event = record2(value, `Native transaction event at line ${line}`); + rejectUnknown2(event, EVENT_KEYS, `Native transaction event at line ${line}`); + if (event.sequence !== line) { + throw new Error(`Native transaction event sequence at line ${line} must be ${line}`); + } + if (!validTimestamp(event.timestamp)) { + throw new Error(`Native transaction event timestamp at line ${line} is invalid`); + } + if (typeof event.type !== "string" || !EVENT_TYPES.has(event.type)) { + throw new Error(`Native transaction event type at line ${line} is invalid`); + } + const operationEvent = event.type === "operation-started" || event.type === "operation-completed"; + if (operationEvent && (typeof event.operationId !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(event.operationId) || Buffer.byteLength(event.operationId, "utf8") > 256) || !operationEvent && event.operationId !== void 0) { + throw new Error(`Native transaction event operationId at line ${line} is invalid`); + } + return { + sequence: event.sequence, + timestamp: event.timestamp, + type: event.type, + ...typeof event.operationId === "string" ? { operationId: event.operationId } : {} + }; +} +function matchesIsoTimestampPrefix(value) { + if (value.length > 24) return false; + const shape = "0000-00-00T00:00:00.000Z"; + for (let index = 0; index < value.length; index += 1) { + const expected = shape[index]; + const actual = value[index]; + if (expected === "0") { + if (!/[0-9]/u.test(actual)) return false; + } else if (actual !== expected) { + return false; + } + } + const numericRanges = [ + [5, 2, 1, 12], + [8, 2, 1, 31], + [11, 2, 0, 23], + [14, 2, 0, 59], + [17, 2, 0, 59], + [20, 3, 0, 999] + ]; + for (const [offset, width, minimum, maximum] of numericRanges) { + if (value.length <= offset) continue; + const partial = value.slice(offset, Math.min(offset + width, value.length)); + const completable = Array.from( + { length: maximum - minimum + 1 }, + (_entry, index) => String(minimum + index).padStart(width, "0") + ).some((candidate) => candidate.startsWith(partial)); + if (!completable) return false; + } + if (value.length >= 10 && !validTimestamp(`${value.slice(0, 10)}T00:00:00.000Z`)) { + return false; + } + return value.length < 24 || validTimestamp(value); +} +function isStrictLiteralPrefix(value, expected) { + return value.length < expected.length && expected.startsWith(value); +} +function couldCompleteOperationEventSuffix(value) { + const operationPrefix = '","operationId":"'; + if (isStrictLiteralPrefix(value, operationPrefix)) return true; + if (!value.startsWith(operationPrefix)) return false; + const remainder = value.slice(operationPrefix.length); + const closingQuote = remainder.indexOf('"'); + if (closingQuote === -1) { + return remainder.length === 0 || /^[a-z0-9][a-z0-9-]*$/u.test(remainder); + } + const operationId = remainder.slice(0, closingQuote); + if (!/^[a-z0-9][a-z0-9-]*$/u.test(operationId)) return false; + return isStrictLiteralPrefix(remainder.slice(closingQuote), '"}'); +} +function isRecognizedIncompleteEventTail(source, sequence) { + if (source.length === 0 || Buffer.byteLength(source, "utf8") > NATIVE_TRANSACTION_EVENT_MAX_BYTES) { + return false; + } + const prefix = `{"sequence":${sequence},"timestamp":"`; + if (isStrictLiteralPrefix(source, prefix)) return true; + if (!source.startsWith(prefix)) return false; + const afterPrefix = source.slice(prefix.length); + const timestamp2 = afterPrefix.slice(0, Math.min(24, afterPrefix.length)); + if (!matchesIsoTimestampPrefix(timestamp2)) return false; + if (afterPrefix.length < 24) return true; + if (!validTimestamp(timestamp2)) return false; + const typePrefix = '","type":"'; + const afterTimestamp = afterPrefix.slice(24); + if (isStrictLiteralPrefix(afterTimestamp, typePrefix)) return true; + if (!afterTimestamp.startsWith(typePrefix)) return false; + const typeAndSuffix = afterTimestamp.slice(typePrefix.length); + for (const type of EVENT_TYPES) { + if (isStrictLiteralPrefix(typeAndSuffix, type)) return true; + if (!typeAndSuffix.startsWith(type)) continue; + const suffix = typeAndSuffix.slice(type.length); + if (type === "operation-started" || type === "operation-completed") { + if (couldCompleteOperationEventSuffix(suffix)) return true; + } else if (isStrictLiteralPrefix(suffix, '"}')) { + return true; + } + } + return false; +} +function parseEventLine(source, line) { + if (source.length === 0) throw new Error("Blank transaction event line"); + if (Buffer.byteLength(source, "utf8") > NATIVE_TRANSACTION_EVENT_MAX_BYTES) { + throw new Error(`Native transaction event at line ${line} is too large`); + } + return parseEvent(JSON.parse(source), line); +} +function parseEventLogSource(source) { + const entries = source.split("\n"); + const terminated = entries.at(-1) === ""; + if (terminated) entries.pop(); + if (entries.length > NATIVE_TRANSACTION_EVENT_MAX_COUNT) { + throw new Error( + `Native transaction event log exceeds ${NATIVE_TRANSACTION_EVENT_MAX_COUNT} events` + ); + } + const events = []; + for (let index = 0; index < entries.length; index += 1) { + const line = index + 1; + const raw = entries[index]; + const entry2 = raw.endsWith("\r") ? raw.slice(0, -1) : raw; + const finalUnterminated = !terminated && index === entries.length - 1; + try { + events.push(parseEventLine(entry2, line)); + } catch (error) { + if (finalUnterminated) { + let syntacticallyComplete = false; + try { + JSON.parse(entry2); + syntacticallyComplete = true; + } catch { + } + if (!syntacticallyComplete && isRecognizedIncompleteEventTail(entry2, line)) break; + } + throw new Error(`Invalid Native transaction event at line ${line}`, { cause: error }); + } + } + return events; +} +function canonicalEventLogSource(events) { + return events.length === 0 ? "" : `${events.map((event) => JSON.stringify(event)).join("\n")} +`; +} +function transactionDir(paths, id) { + if (!/^[a-f0-9-]{8,}$/u.test(id)) throw new Error(`Invalid Native transaction id: ${id}`); + return path8.join(paths.transactionsDir, id); +} +function nativeTransactionPaths(paths, id) { + const directory = transactionDir(paths, id); + return { + directory, + journal: path8.join(directory, "transaction.json"), + events: path8.join(directory, "events.jsonl"), + staged: path8.join(directory, "staged"), + backups: path8.join(directory, "backups") + }; +} +async function resolveNativeTransactionPaths(paths, id) { + const transaction = nativeTransactionPaths(paths, id); + await Promise.all( + Object.values(transaction).map( + (target) => resolveContainedNativePath(paths.nativeRoot, target) + ) + ); + return transaction; +} +function resolveRefLexically(paths, ref) { + if (ref.length === 0 || path8.isAbsolute(ref) || /^(?:[A-Za-z]:|~|[\\/])/u.test(ref) || ref.split(/[\\/]/u).includes("..")) { + throw new Error(`Unsafe Native transaction ref: ${ref}`); + } + const target = path8.resolve(paths.nativeRoot, ...ref.split(/[\\/]/u)); + if (!isInsidePath(paths.nativeRoot, target)) + throw new Error(`Unsafe Native transaction ref: ${ref}`); + return target; +} +async function resolveRef(paths, ref) { + return resolveContainedNativePath(paths.nativeRoot, resolveRefLexically(paths, ref)); +} +async function exists(file) { + try { + await fs8.access(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function readEventLogSnapshot(paths, id, options = {}) { + const tx = await resolveNativeTransactionPaths(paths, id); + try { + await fs8.lstat(tx.events); + } catch (error) { + if (error.code === "ENOENT") { + return { + exists: false, + hash: null, + size: 0, + events: [], + canonicalSource: "", + needsRepair: false + }; + } + throw error; + } + try { + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.events, + maxBytes: NATIVE_TRANSACTION_EVENTS_MAX_BYTES, + label: `Native transaction event log ${id}`, + hooks: options.hooks + }); + let source; + try { + source = UTF8_DECODER.decode(snapshot2.bytes); + } catch (error) { + throw new Error(`Native transaction event log ${id} is not valid UTF-8`, { cause: error }); + } + const events = parseEventLogSource(source); + const canonicalSource = canonicalEventLogSource(events); + return { + exists: true, + hash: snapshot2.hash, + size: snapshot2.size, + events, + canonicalSource, + needsRepair: source !== canonicalSource + }; + } catch (error) { + if (error.code === "ENOENT") { + throw new Error(`Native transaction event log ${id} changed while reading`, { + cause: error + }); + } + throw error; + } +} +async function assertEventLogSnapshotUnchanged(paths, id, expected) { + const actual = await readEventLogSnapshot(paths, id); + if (actual.exists !== expected.exists || actual.hash !== expected.hash || actual.size !== expected.size) { + throw new Error(`Native transaction event log ${id} changed before append`); + } +} +async function appendNativeTransactionEvent(paths, id, type, operationId) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readEventLogSnapshot(paths, id); + const existing = snapshot2.events.find( + (event2) => event2.type === type && event2.operationId === operationId + ); + if (existing) { + if (snapshot2.needsRepair) { + await atomicWriteText(tx.events, snapshot2.canonicalSource, { + containedRoot: paths.nativeRoot, + beforeCommit: () => assertEventLogSnapshotUnchanged(paths, id, snapshot2) + }); + } + return existing; + } + if (snapshot2.events.length >= NATIVE_TRANSACTION_EVENT_MAX_COUNT) { + throw new Error( + `Native transaction event log ${id} exceeds ${NATIVE_TRANSACTION_EVENT_MAX_COUNT} events` + ); + } + const event = { + sequence: snapshot2.events.length + 1, + timestamp: (/* @__PURE__ */ new Date()).toISOString(), + type, + ...operationId ? { operationId } : {} + }; + parseEvent(event, event.sequence); + await fs8.mkdir(tx.directory, { recursive: true }); + await atomicWriteText(tx.events, canonicalEventLogSource([...snapshot2.events, event]), { + containedRoot: paths.nativeRoot, + beforeCommit: () => assertEventLogSnapshotUnchanged(paths, id, snapshot2) + }); + return event; +} +async function createNativeTransaction(paths, journal) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + journal = parseJournal(journal); + const tx = await resolveNativeTransactionPaths(paths, journal.id); + await fs8.mkdir(tx.staged, { recursive: true }); + await fs8.mkdir(tx.backups, { recursive: true }); + await atomicWriteJson(tx.journal, journal); + await appendNativeTransactionEvent(paths, journal.id, "prepared"); +} +async function readNativeTransaction(paths, id, options = {}) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.journal, + maxBytes: NATIVE_TRANSACTION_JOURNAL_MAX_BYTES, + label: `Native transaction journal ${id}`, + hooks: options.hooks + }); + const value = JSON.parse(UTF8_DECODER.decode(snapshot2.bytes)); + const journal = parseJournal(value); + if (journal.id !== id) { + throw new Error(`Invalid Native transaction journal: ${id}`); + } + return journal; +} +async function readNativeTransactionEvents(paths, id, options = {}) { + return (await readEventLogSnapshot(paths, id, options)).events; +} +async function setNativeTransactionStatus(paths, journal, status) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + const updated = parseJournal({ ...journal, status }); + await atomicWriteJson((await resolveNativeTransactionPaths(paths, journal.id)).journal, updated); + return updated; +} +async function readLegacyTransactionFile(paths, file, label) { + try { + return await readNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + label + }); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function copyAtomic(paths, source, target, label) { + const sourceSnapshot = await readLegacyTransactionFile(paths, source, `${label} source`); + if (!sourceSnapshot) throw new Error(`${label} source does not exist`); + const targetSnapshot = await readLegacyTransactionFile(paths, target, `${label} target`); + await ensureNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: path8.dirname(target), + label: `${label} target parent` + }); + await copyNativeProtectedFile({ + sourceRoot: paths.nativeRoot, + source, + targetRoot: paths.nativeRoot, + target, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + label, + expectedHash: sourceSnapshot.hash, + expectedTargetHash: targetSnapshot?.hash ?? null, + exclusive: targetSnapshot === null + }); +} +async function removeLegacyTransactionFile(paths, file, label) { + const snapshot2 = await readLegacyTransactionFile(paths, file, label); + if (!snapshot2) return; + await removeNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + expectedHash: snapshot2.hash, + expectedSize: snapshot2.size, + label + }); +} +async function backupTarget(paths, operation) { + if (!operation.backup) return; + const target = await resolveRef(paths, operation.target); + const backup = await resolveRef(paths, operation.backup); + if (!await exists(target) || await exists(backup)) return; + await copyAtomic(paths, target, backup, `Legacy Native transaction backup ${operation.id}`); +} +async function applyOperation(paths, operation) { + const target = await resolveRef(paths, operation.target); + if (operation.type === "write") { + if (!operation.staged) throw new Error(`Write operation ${operation.id} has no staged ref`); + await backupTarget(paths, operation); + await copyAtomic( + paths, + await resolveRef(paths, operation.staged), + target, + `Legacy Native transaction write ${operation.id}` + ); + return; + } + if (operation.type === "remove") { + await backupTarget(paths, operation); + await removeLegacyTransactionFile( + paths, + target, + `Legacy Native transaction remove ${operation.id}` + ); + return; + } + if (!operation.source) throw new Error(`Move operation ${operation.id} has no source ref`); + const source = await resolveRef(paths, operation.source); + const [sourceExists, targetExists] = await Promise.all([exists(source), exists(target)]); + if (!sourceExists && targetExists) { + const targetDirectory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: target, + label: `Legacy Native transaction move target ${operation.id}`, + maxEntries: NATIVE_LEGACY_TRANSACTION_DIRECTORY_MAX_ENTRIES + }); + await targetDirectory.verify(); + return; + } + if (targetExists) throw new Error(`Move target already exists: ${operation.target}`); + if (!sourceExists) throw new Error(`Move source does not exist: ${operation.source}`); + await moveNativeProtectedDirectory({ + root: paths.nativeRoot, + source, + target, + label: `Legacy Native transaction move ${operation.id}` + }); +} +async function applyNativeTransaction(paths, journal, hooks) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + let current = journal.status === "prepared" ? await setNativeTransactionStatus(paths, journal, "applying") : journal; + const events = await readNativeTransactionEvents(paths, journal.id); + const completed = new Set( + events.filter((event) => event.type === "operation-completed").map((event) => event.operationId) + ); + let completedCount = completed.size; + for (const operation of current.operations) { + if (completed.has(operation.id)) continue; + await appendNativeTransactionEvent(paths, current.id, "operation-started", operation.id); + await applyOperation(paths, operation); + await appendNativeTransactionEvent(paths, current.id, "operation-completed", operation.id); + completedCount += 1; + await hooks?.afterOperation?.(operation, completedCount); + } + current = await readNativeTransaction(paths, current.id); + return current; +} +async function rollbackOperation(paths, operation) { + const target = await resolveRef(paths, operation.target); + const backup = operation.backup ? await resolveRef(paths, operation.backup) : null; + if (operation.type === "move") { + if (!operation.source) throw new Error(`Move operation ${operation.id} has no source ref`); + const source = await resolveRef(paths, operation.source); + if (await exists(target)) { + if (await exists(source)) { + throw new Error(`Legacy Native rollback source already exists: ${operation.source}`); + } + await moveNativeProtectedDirectory({ + root: paths.nativeRoot, + source: target, + target: source, + label: `Legacy Native transaction rollback move ${operation.id}` + }); + } + return; + } + if (backup && await exists(backup)) { + await copyAtomic( + paths, + backup, + target, + `Legacy Native transaction rollback restore ${operation.id}` + ); + } else { + await removeLegacyTransactionFile( + paths, + target, + `Legacy Native transaction rollback remove ${operation.id}` + ); + } +} +async function rollbackNativeTransaction(paths, journal) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some( + (event) => event.type === "archive-finalization-started" || event.type === "archive-finalized" + )) { + throw new Error("An archive whose finalization started can only be recovered by continuing it"); + } + let current = await setNativeTransactionStatus(paths, journal, "rolling-back"); + await appendNativeTransactionEvent(paths, current.id, "rollback-started"); + const started = new Set( + events.filter((event) => event.type === "operation-started" || event.type === "operation-completed").map((event) => event.operationId) + ); + for (const operation of [...current.operations].reverse()) { + if (started.has(operation.id)) await rollbackOperation(paths, operation); + } + await appendNativeTransactionEvent(paths, current.id, "rollback-completed"); + current = await setNativeTransactionStatus(paths, current, "rolled-back"); + return current; +} +async function finalizeNativeTransaction(paths, journal, event) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + await appendNativeTransactionEvent(paths, journal.id, event); + return event === "commit" ? setNativeTransactionStatus(paths, journal, "committed") : journal; +} +function nativeRootRef(paths, target) { + const absolute = path8.resolve(target); + if (!isInsidePath(paths.nativeRoot, absolute)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + return path8.relative(paths.nativeRoot, absolute).split(path8.sep).join("/"); +} + +// domains/comet-native/native-mutation-lock.ts +async function hasUnfinishedTransaction(paths, allowedTransactionId) { + let entries; + try { + entries = await fs9.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + try { + const transaction = await readNativeTransaction(paths, entry2.name); + if (transaction.id !== allowedTransactionId && transaction.status !== "committed" && transaction.status !== "rolled-back") { + return true; + } + } catch { + return true; + } + } + return false; +} +async function acquireNativeMutationLock(paths, operation) { + const deadline = Date.now() + 5e3; + const file = path9.join(paths.locksDir, "root-move.lock"); + while (true) { + try { + return await acquireNativeLock(paths, "root-move", operation); + } catch (error) { + const cause = error.cause; + if (cause?.code !== "EEXIST") throw error; + const diagnosis = await diagnoseNativeLock(file); + if (diagnosis.status === "missing") continue; + if (diagnosis.status !== "active" || Date.now() >= deadline) throw error; + await new Promise((resolve) => setTimeout(resolve, 5 + Math.floor(Math.random() * 11))); + } + } +} +async function withNativeMutationLock(paths, operation, work, options) { + const lock = await acquireNativeMutationLock(paths, operation); + try { + await assertNoPendingNativeRootMove(paths.projectRoot); + if (await hasUnfinishedTransaction(paths, options?.allowedTransactionId)) { + throw new Error("Native transaction recovery is required before another mutation"); + } + return await work(); + } finally { + await releaseNativeLock(lock); + } +} + +// domains/comet-native/native-revision.ts +function validRevision(value) { + return Number.isSafeInteger(value) && value >= 1; +} +async function compareAndSwapNativeRevision(options) { + if (!validRevision(options.expectedRevision)) { + throw new Error("Native expected revision must be a positive integer"); + } + if (options.next.revision !== options.expectedRevision + 1) { + throw new Error("Native CAS next revision must increment the expected revision exactly once"); + } + const current = await options.read(); + if (!validRevision(current.revision)) { + throw new Error("Native current revision must be a positive integer"); + } + const equals = options.equals ?? ((left, right) => JSON.stringify(left) === JSON.stringify(right)); + if (current.revision === options.next.revision && equals(current, options.next)) { + return current; + } + if (current.revision !== options.expectedRevision) { + throw options.conflict(current.revision); + } + await options.write(options.next); + return options.next; +} + +// domains/comet-native/native-snapshot.ts +import { createHash as createHash5 } from "crypto"; +import { spawn } from "node:child_process"; +import { promises as fs10 } from "fs"; +import path10 from "path"; + +// domains/comet-native/native-hash.ts +import { createHash as createHash4 } from "crypto"; +import { createReadStream } from "fs"; +async function sha256File(file) { + const hash6 = createHash4("sha256"); + for await (const chunk of createReadStream(file)) hash6.update(chunk); + return hash6.digest("hex"); +} +function sha256Text(content) { + return createHash4("sha256").update(content).digest("hex"); +} + +// domains/comet-native/native-snapshot.ts +var DEFAULT_NATIVE_SNAPSHOT_LIMITS = { + maxFiles: 1e4, + maxFileBytes: 5 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxManifestBytes: 1024 * 1024 +}; +var MAX_RECORDED_OMISSIONS = 1e3; +var NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES = 8 * 1024 * 1024; +var CHANGE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var MANIFEST_KEYS = /* @__PURE__ */ new Set([ + "schema", + "origin", + "capture", + "createdAt", + "complete", + "limits", + "entries", + "omitted", + "omittedCount", + "omissionOverflow", + "policy" +]); +var LIMIT_KEYS = /* @__PURE__ */ new Set([ + "maxFiles", + "maxFileBytes", + "maxTotalBytes", + "maxManifestBytes", + "maxDurationMs" +]); +var POLICY_KEYS = /* @__PURE__ */ new Set(["schema", "include", "exclude", "hash"]); +var CAPTURE_KEYS = /* @__PURE__ */ new Set(["provider", "gitSelection", "physicalSelection", "projection"]); +var GIT_PROJECTION_KEYS = /* @__PURE__ */ new Set(["provider", "selection"]); +var GIT_SELECTION_KEYS = /* @__PURE__ */ new Set([ + "schema", + "status", + "stageBefore", + "combined", + "stageAfter", + "finalStageBefore", + "finalCombined", + "finalStageAfter" +]); +var GIT_SELECTION_STREAM_KEYS = /* @__PURE__ */ new Set([ + "hash", + "recordCount", + "storedRecordCount", + "stdoutBytes", + "overflow" +]); +var PHYSICAL_SELECTION_KEYS = /* @__PURE__ */ new Set(["schema", "status", "before", "after"]); +var PHYSICAL_SELECTION_STREAM_KEYS = /* @__PURE__ */ new Set([ + "hash", + "visitedNodeCount", + "recordCount", + "storedRecordCount", + "encodedBytes", + "overflow", + "unstable" +]); +var ENTRY_KEYS = /* @__PURE__ */ new Set(["path", "hash", "size", "type"]); +var OMISSION_KEYS = /* @__PURE__ */ new Set(["path", "size", "type", "reason"]); +var OMISSION_OVERFLOW_KEYS = /* @__PURE__ */ new Set(["ref", "hash", "count"]); +var SNAPSHOT_ORIGINS = /* @__PURE__ */ new Set([ + "change-created", + "legacy-migration", + "explicit" +]); +var OMISSION_TYPES = /* @__PURE__ */ new Set(["file", "directory", "other"]); +var OMISSION_REASONS = /* @__PURE__ */ new Set([ + "file-size", + "file-count", + "total-size", + "manifest-size", + "changed-during-read", + "unreadable", + "gitlink-unavailable", + "gitlink-dirty", + "gitlink-changed", + "legacy-gitlink-boundary", + "git-enumeration-limit", + "git-selection-changed", + "physical-enumeration-limit", + "physical-selection-changed" +]); +var HASH_PATTERN = /^[a-f0-9]{64}$/u; +var GIT_OBJECT_ID_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u; +var UNREADABLE_ERROR_CODES = /* @__PURE__ */ new Set(["EACCES", "EPERM"]); +var GIT_LIST_STDERR_LIMIT = 64 * 1024; +var GIT_TEXT_STDOUT_LIMIT = 64 * 1024; +var DEFAULT_NATIVE_SNAPSHOT_EXECUTION_BUDGET_MS = 6e4; +var WINDOWS_TASKKILL_ATTEMPT_MS = 1e3; +var WINDOWS_TASKKILL_ATTEMPTS = 2; +var DEFAULT_NATIVE_GIT_SELECTION_LIMITS = { + maxRecords: 2e4, + maxBytes: 8 * 1024 * 1024, + maxRecordBytes: 64 * 1024 +}; +var DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS = { + maxNodes: 2e4, + maxBytes: 8 * 1024 * 1024, + maxPathBytes: 64 * 1024 +}; +function createNativeSnapshotExecution(options) { + const deadlineMs = options.deadlineMs ?? DEFAULT_NATIVE_SNAPSHOT_EXECUTION_BUDGET_MS; + if (!Number.isSafeInteger(deadlineMs) || deadlineMs < 1) { + throw new Error("Native snapshot execution budget must be a positive integer"); + } + return { + deadlineAt: Date.now() + deadlineMs, + gitProcess: options.gitProcess ?? { command: "git" } + }; +} +function remainingNativeSnapshotTime(execution) { + return Math.max(0, execution.deadlineAt - Date.now()); +} +function nativeSnapshotExecutionHasBudget(execution) { + return remainingNativeSnapshotTime(execution) >= 1; +} +function nativeGitSnapshotTimeoutError(cause) { + const error = cause === void 0 ? new Error("Native snapshot deadline exceeded while waiting for Git") : new Error("Native snapshot deadline exceeded while waiting for Git", { cause }); + return Object.assign(error, { code: "GIT_SNAPSHOT_TIMEOUT" }); +} +function isNativeGitSnapshotTimeout(error) { + return error.code === "GIT_SNAPSHOT_TIMEOUT"; +} +function resolveNativePhysicalSelectionLimits(values) { + const limits = { ...DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS, ...values }; + if (!Number.isSafeInteger(limits.maxNodes) || limits.maxNodes < 1 || !Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 1 || !Number.isSafeInteger(limits.maxPathBytes) || limits.maxPathBytes < 1 || limits.maxPathBytes > limits.maxBytes) { + throw new Error("Native physical selection limits must be positive bounded integers"); + } + return limits; +} +async function terminateNativeProcessTree(child, adapter) { + if (adapter.terminateTree) { + await adapter.terminateTree(child); + return; + } + const pid = child.pid; + if (pid === void 0) { + child.kill("SIGKILL"); + return; + } + if (process.platform === "win32") { + const configuredSystemRoot = process.env.SystemRoot ?? process.env.WINDIR; + const systemRoot = configuredSystemRoot && path10.win32.isAbsolute(configuredSystemRoot) ? path10.win32.resolve(configuredSystemRoot) : "C:\\Windows"; + const taskkill = path10.win32.join(systemRoot, "System32", "taskkill.exe"); + if (!path10.win32.isAbsolute(taskkill)) { + child.kill("SIGKILL"); + child.stdin?.destroy(); + child.stdout?.destroy(); + child.stderr?.destroy(); + throw Object.assign(new Error("Native Git taskkill path is not trusted"), { + code: "GIT_SNAPSHOT_TERMINATION_UNCONFIRMED" + }); + } + const runTaskkillAttempt = () => new Promise((resolve) => { + let finished = false; + let killer = null; + let fallbackTimer = null; + const finish = (confirmed, terminateKiller = false) => { + if (finished) return; + finished = true; + if (fallbackTimer) clearTimeout(fallbackTimer); + if (terminateKiller) killer?.kill("SIGKILL"); + resolve(confirmed); + }; + try { + killer = spawn(taskkill, ["/pid", String(pid), "/t", "/f"], { + stdio: "ignore", + windowsHide: true + }); + killer.once("error", () => finish(false)); + killer.once("close", (code) => finish(code === 0)); + fallbackTimer = setTimeout(() => finish(false, true), WINDOWS_TASKKILL_ATTEMPT_MS); + fallbackTimer.unref(); + } catch { + finish(false); + } + }); + for (let attempt = 0; attempt < WINDOWS_TASKKILL_ATTEMPTS; attempt += 1) { + if (await runTaskkillAttempt()) { + child.kill("SIGKILL"); + return; + } + } + child.kill("SIGKILL"); + child.stdin?.destroy(); + child.stdout?.destroy(); + child.stderr?.destroy(); + throw Object.assign( + new Error("Native Git process-tree termination could not be confirmed on Windows"), + { code: "GIT_SNAPSHOT_TERMINATION_UNCONFIRMED" } + ); + } + try { + process.kill(-pid, "SIGKILL"); + } catch (error) { + if (error.code !== "ESRCH") throw error; + } + child.kill("SIGKILL"); +} +function startNativeGitProcess(execution, projectRoot, args, input) { + const remaining = remainingNativeSnapshotTime(execution); + if (remaining < 1) throw nativeGitSnapshotTimeoutError(); + const adapter = execution.gitProcess; + const child = spawn( + adapter.command, + [...adapter.argsPrefix ?? [], "-C", projectRoot, ...args], + { + stdio: [input ? "pipe" : "ignore", "pipe", "pipe"], + windowsHide: true, + detached: process.platform !== "win32" + } + ); + let spawnError = null; + let timedOut = false; + let termination = null; + child.once("error", (error) => { + spawnError = error; + }); + const close = new Promise((resolve) => { + child.once("close", resolve); + }); + const timer = setTimeout(() => { + timedOut = true; + termination = terminateNativeProcessTree(child, adapter).then( + () => ({ error: null }), + (error) => ({ error }) + ); + }, remaining); + timer.unref(); + const completion = close.then(async (code) => { + clearTimeout(timer); + if (timedOut) { + const result2 = await termination; + if (result2?.error) throw nativeGitSnapshotTimeoutError(result2.error); + throw nativeGitSnapshotTimeoutError(); + } + return { code, spawnError }; + }); + return { child, completion }; +} +function resolveNativeGitSelectionLimits(values) { + const limits = { ...DEFAULT_NATIVE_GIT_SELECTION_LIMITS, ...values }; + if (!Number.isSafeInteger(limits.maxRecords) || limits.maxRecords < 1 || !Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 1 || !Number.isSafeInteger(limits.maxRecordBytes) || limits.maxRecordBytes < 1 || limits.maxRecordBytes > limits.maxBytes) { + throw new Error("Native Git selection limits must be positive bounded integers"); + } + return limits; +} +async function runGitBoundedOutput(execution, projectRoot, args, maxBytes = GIT_TEXT_STDOUT_LIMIT) { + const { child, completion } = startNativeGitProcess(execution, projectRoot, args, false); + const stdout = []; + const stderr = []; + let stderrBytes = 0; + let stdoutBytes = 0; + let stdoutOverflow = false; + child.stdout.on("data", (chunk) => { + const remaining = Math.max(0, maxBytes - stdoutBytes); + if (remaining > 0) stdout.push(Buffer.from(chunk).subarray(0, remaining)); + stdoutBytes += chunk.byteLength; + if (stdoutBytes > maxBytes) stdoutOverflow = true; + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + const { code, spawnError } = await completion; + if (code === 0 && spawnError === null && !stdoutOverflow) return Buffer.concat(stdout); + throw Object.assign( + new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ), + { code: "GIT_SNAPSHOT_UNAVAILABLE" } + ); +} +function gitSelectionStreamEvidence(result2) { + return { + hash: result2.digest, + recordCount: result2.recordCount, + storedRecordCount: result2.records.length, + stdoutBytes: result2.stdoutBytes, + overflow: result2.overflow + }; +} +function runGitNullRecords(execution, projectRoot, args, options) { + if (options.outputChunkBytes !== void 0 && (!Number.isSafeInteger(options.outputChunkBytes) || options.outputChunkBytes < 1)) { + throw new Error("Native Git output chunk size must be a positive integer"); + } + return new Promise((resolve, reject) => { + const { child, completion } = startNativeGitProcess( + execution, + projectRoot, + args, + options.stdin !== void 0 + ); + const records = []; + const digest = createHash5("sha256"); + const stderr = []; + let stderrBytes = 0; + let stdoutBytes = 0; + let storedBytes = 0; + let recordCount = 0; + let pending = Buffer.alloc(0); + let droppingRecord = false; + let overflow = false; + let malformed = false; + const consumeChunk = (chunk) => { + let offset = 0; + while (offset < chunk.byteLength) { + const separator = chunk.indexOf(0, offset); + const end = separator < 0 ? chunk.byteLength : separator; + const part = chunk.subarray(offset, end); + if (!droppingRecord && part.byteLength > 0) { + if (pending.byteLength + part.byteLength > options.maxRecordBytes) { + pending = Buffer.alloc(0); + droppingRecord = true; + overflow = true; + } else { + pending = Buffer.concat([pending, part]); + } + } + if (separator < 0) break; + recordCount += 1; + if (pending.byteLength === 0 && !droppingRecord) malformed = true; + const recordBytes = pending.byteLength + 1; + if (!droppingRecord && recordCount <= options.maxRecords && storedBytes + recordBytes <= options.maxBytes) { + records.push(pending); + storedBytes += recordBytes; + } else { + overflow = true; + } + pending = Buffer.alloc(0); + droppingRecord = false; + offset = separator + 1; + } + }; + child.stdout.on("data", (chunk) => { + digest.update(chunk); + stdoutBytes += chunk.byteLength; + if (stdoutBytes > options.maxBytes) overflow = true; + const outputChunkBytes = options.outputChunkBytes; + if (outputChunkBytes === void 0) { + consumeChunk(chunk); + return; + } + for (let offset = 0; offset < chunk.byteLength; offset += outputChunkBytes) { + consumeChunk(chunk.subarray(offset, Math.min(chunk.byteLength, offset + outputChunkBytes))); + } + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + child.stdin?.on("error", () => { + }); + void completion.then(({ code, spawnError }) => { + const acceptedExitCodes = options.acceptedExitCodes ?? [0]; + if (code !== null && acceptedExitCodes.includes(code) && spawnError === null && !malformed && pending.byteLength === 0 && !droppingRecord) { + resolve({ + records, + digest: digest.digest("hex"), + overflow, + recordCount, + stdoutBytes + }); + return; + } + reject( + new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : malformed || pending.byteLength > 0 || droppingRecord ? ": malformed NUL-delimited output" : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ) + ); + }, reject); + if (options.stdin) child.stdin?.end(options.stdin); + }); +} +function decodeGitRecord(value) { + const decoded = value.toString("utf8"); + if (!Buffer.from(decoded, "utf8").equals(value)) { + throw new Error("Native Git snapshot provider returned non-UTF-8 path data"); + } + return decoded; +} +async function runGitCheckIgnore(execution, projectRoot, paths) { + if (paths.length === 0) return /* @__PURE__ */ new Set(); + const result2 = await runGitNullRecords( + execution, + projectRoot, + ["check-ignore", "--no-index", "-z", "--stdin"], + { + ...DEFAULT_NATIVE_GIT_SELECTION_LIMITS, + maxRecords: Math.max(1, paths.length), + acceptedExitCodes: [0, 1], + stdin: Buffer.from(`${paths.join("\0")}\0`, "utf8") + } + ); + if (result2.overflow) { + throw new Error("Native Git check-ignore output exceeded its safety budget"); + } + return new Set( + result2.records.map((record8) => { + const value = decodeGitRecord(record8); + return value.endsWith("/") ? value.slice(0, -1) : value; + }) + ); +} +async function runGitHasOutput(execution, projectRoot, args) { + const { child, completion } = startNativeGitProcess(execution, projectRoot, args, false); + const stderr = []; + let stderrBytes = 0; + let hasOutput = false; + child.stdout.on("data", (chunk) => { + if (chunk.byteLength > 0) hasOutput = true; + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + const { code, spawnError } = await completion; + if (code === 0 && spawnError === null) return hasOutput; + throw new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ); +} +function safeGitProjectPath(value) { + const withoutDirectoryMarker = value.endsWith("/") ? value.slice(0, -1) : value; + if (withoutDirectoryMarker.length === 0 || withoutDirectoryMarker.includes("\\") || path10.posix.isAbsolute(withoutDirectoryMarker) || /^[A-Za-z]:/u.test(withoutDirectoryMarker) || path10.posix.normalize(withoutDirectoryMarker) !== withoutDirectoryMarker || withoutDirectoryMarker === ".." || withoutDirectoryMarker.startsWith("../") || withoutDirectoryMarker.includes("\0")) { + return null; + } + return withoutDirectoryMarker; +} +function requireSafeGitProjectPaths(values, source) { + return values.map((value) => { + const relative = safeGitProjectPath(value); + if (relative === null) { + throw new Error(`Native Git snapshot provider returned an unsafe ${source} path`); + } + return relative; + }); +} +async function hasGitMetadataBoundary(projectRoot) { + let cursor = path10.resolve(projectRoot); + while (true) { + try { + await fs10.lstat(path10.join(cursor, ".git")); + return true; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const parent = path10.dirname(cursor); + if (parent === cursor) return false; + cursor = parent; + } +} +async function readNativeGitSelectionResults(execution, projectRoot, limits, hooks = {}) { + const options = { + ...limits, + ...hooks.outputChunkBytes === void 0 ? {} : { outputChunkBytes: hooks.outputChunkBytes } + }; + const stagedBefore = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--stage", "-z"], + options + ); + await hooks.afterStageBefore?.(); + const combined = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], + options + ); + await hooks.afterCombined?.(); + const stagedAfter = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--stage", "-z"], + options + ); + return { stagedBefore, combined, stagedAfter }; +} +function gitSelectionFence(results) { + return { + stageBefore: gitSelectionStreamEvidence(results.stagedBefore), + combined: gitSelectionStreamEvidence(results.combined), + stageAfter: gitSelectionStreamEvidence(results.stagedAfter) + }; +} +async function nativeGitSnapshotSelection(execution, projectRoot, limits = DEFAULT_NATIVE_GIT_SELECTION_LIMITS, hooks = {}) { + if (!await hasGitMetadataBoundary(projectRoot)) return null; + let insideWorktree; + try { + insideWorktree = await runGitBoundedOutput(execution, projectRoot, [ + "rev-parse", + "--is-inside-work-tree" + ]); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider could not inspect the repository boundary", { + cause: error + }); + } + if (insideWorktree.toString("utf8").trim() !== "true") { + throw new Error("Native Git snapshot provider found .git metadata outside a working tree"); + } + let results; + try { + results = await readNativeGitSelectionResults(execution, projectRoot, limits, hooks); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider failed after repository detection", { + cause: error + }); + } + const { stagedBefore, combined, stagedAfter } = results; + const tracked = /* @__PURE__ */ new Set(); + const gitlinks = /* @__PURE__ */ new Set(); + const addStagedRecords = (records) => { + for (const encoded of records) { + const record8 = decodeGitRecord(encoded); + const separator = record8.indexOf(" "); + if (separator < 0) { + throw new Error("Native Git snapshot provider returned a malformed staged record"); + } + const header = /^(?[0-7]{6}) (?[a-f0-9]{40}|[a-f0-9]{64}) (?[0-3])$/u.exec( + record8.slice(0, separator) + )?.groups; + if (!header) { + throw new Error("Native Git snapshot provider returned a malformed staged header"); + } + const relative = safeGitProjectPath(record8.slice(separator + 1)); + if (relative === null) { + throw new Error("Native Git snapshot provider returned an unsafe staged path"); + } + tracked.add(relative); + if (header.mode === "160000" && header.stage === "0") gitlinks.add(relative); + } + }; + addStagedRecords(stagedBefore.records); + addStagedRecords(stagedAfter.records); + const combinedRecords = combined.records.map(decodeGitRecord); + const combinedPaths = requireSafeGitProjectPaths(combinedRecords, "combined"); + const untracked = new Set(combinedPaths.filter((relative) => !tracked.has(relative))); + const nestedRepositories = new Set( + combinedPaths.filter( + (relative, index) => !tracked.has(relative) && combinedRecords[index].endsWith("/") + ) + ); + for (const gitlink of gitlinks) nestedRepositories.delete(gitlink); + return { + tracked, + untracked, + gitlinks, + nestedRepositories, + omissions: [], + overflow: null, + evidence: null, + initialFence: gitSelectionFence(results) + }; +} +function sameGitSelectionStream(left, right) { + return left.hash === right.hash && left.recordCount === right.recordCount && left.storedRecordCount === right.storedRecordCount && left.stdoutBytes === right.stdoutBytes && left.overflow === right.overflow; +} +function gitSelectionFenceOverflow(fence) { + return fence.stageBefore.overflow || fence.combined.overflow || fence.stageAfter.overflow; +} +function gitSelectionChanged(initial, final) { + return !sameGitSelectionStream(initial.stageBefore, initial.stageAfter) || !sameGitSelectionStream(initial.stageAfter, final.stageBefore) || !sameGitSelectionStream(initial.combined, final.combined) || !sameGitSelectionStream(final.stageBefore, final.stageAfter); +} +async function finalizeNativeGitSnapshotSelection(execution, projectRoot, limits, selection, outputChunkBytes) { + let finalResults; + try { + finalResults = await readNativeGitSelectionResults(execution, projectRoot, limits, { + ...outputChunkBytes === void 0 ? {} : { outputChunkBytes } + }); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider failed during its final selection fence", { + cause: error + }); + } + const initial = selection.initialFence; + const final = gitSelectionFence(finalResults); + const hasOverflow = gitSelectionFenceOverflow(initial) || gitSelectionFenceOverflow(final); + const changed = gitSelectionChanged(initial, final); + if (hasOverflow) { + selection.omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "git-enumeration-limit" + }); + } + if (changed) { + selection.omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "git-selection-changed" + }); + } + selection.evidence = hasOverflow || changed ? { + schema: "comet.native.git-selection.v1", + status: hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed", + ...initial, + finalStageBefore: final.stageBefore, + finalCombined: final.combined, + finalStageAfter: final.stageAfter + } : null; + if (initial.combined.overflow || final.combined.overflow) { + selection.overflow = { + count: Math.max( + 1, + initial.combined.recordCount - initial.combined.storedRecordCount, + final.combined.recordCount - final.combined.storedRecordCount + ), + hash: sha256Text( + `comet.native.git-selection-overflow.v2 +${JSON.stringify({ + initial: initial.combined, + final: final.combined + })}` + ) + }; + } +} +var PHYSICAL_SELECTION_SUM_MASK = (1n << 256n) - 1n; +function physicalSelectionRecordType(stat) { + if (stat.isFile()) return "file"; + if (stat.isDirectory()) return "directory"; + if (stat.isSymbolicLink()) return "symlink"; + return "other"; +} +async function nativePhysicalSnapshotSelection(options) { + const projectRoot = path10.resolve(options.paths.projectRoot); + const nativeRoot = path10.resolve(options.paths.nativeRoot); + const configFile = path10.resolve(options.paths.configFile); + const selectionFile = path10.join(projectRoot, ".comet", "current-change.json"); + const records = []; + const omissions = []; + const xor = Buffer.alloc(32); + let sum = 0n; + let visitedNodeCount = 0; + let recordCount = 0; + let encodedBytes = 0; + let overflow = false; + let unstable = false; + let stopped = false; + const hasExecutionBudget = () => { + if (remainingNativeSnapshotTime(options.execution) >= 1) return true; + overflow = true; + stopped = true; + return false; + }; + const addRecord = (record8) => { + const encoded = Buffer.from(`${record8.type}\0${record8.path}`, "utf8"); + const digest = createHash5("sha256").update("comet.native.physical-selection-record.v1\0").update(encoded).digest(); + for (let index = 0; index < xor.length; index += 1) xor[index] ^= digest[index]; + sum = sum + BigInt(`0x${digest.toString("hex")}`) & PHYSICAL_SELECTION_SUM_MASK; + recordCount += 1; + encodedBytes += encoded.byteLength; + if (Buffer.byteLength(record8.path, "utf8") > options.limits.maxPathBytes || encodedBytes > options.limits.maxBytes) { + overflow = true; + stopped = true; + return; + } + records.push(record8); + }; + const visit = async (directory) => { + if (stopped) return; + if (!hasExecutionBudget()) return; + let handle; + try { + handle = await fs10.opendir(directory); + } catch (error) { + if (!hasExecutionBudget()) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + if (directory === projectRoot && isUnreadableError(error)) throw error; + unstable ||= isChangedDuringReadError(error); + overflow ||= isUnreadableError(error); + stopped = true; + return; + } + if (!hasExecutionBudget()) { + try { + await handle.close(); + } catch (error) { + if (error.code !== "ERR_DIR_CLOSED") throw error; + } + return; + } + let traversalFailed = false; + let traversalError; + try { + while (!stopped) { + if (!hasExecutionBudget()) break; + let child; + try { + child = await handle.read(); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + if (directory === projectRoot && isUnreadableError(error)) throw error; + unstable ||= isChangedDuringReadError(error); + overflow ||= isUnreadableError(error); + stopped = true; + break; + } + if (!hasExecutionBudget() || child === null) break; + visitedNodeCount += 1; + if (visitedNodeCount > options.limits.maxNodes) { + overflow = true; + stopped = true; + break; + } + const target = path10.join(directory, child.name); + const relative = portableRelative(projectRoot, target); + if (target === configFile || target === selectionFile || sameOrInside(nativeRoot, target) || options.denylist.some((denied) => sameOrInside(denied, target)) || nativeSensitiveRelativePathReason(relative) !== null) { + continue; + } + if (!hasExecutionBudget()) break; + let stat; + try { + stat = await fs10.lstat(target); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + unstable = true; + addRecord({ path: relative, type: "other" }); + omissions.push({ + path: relative, + size: null, + type: "file", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + await options.hooks?.afterNode?.(relative); + if (stopped) break; + if (!hasExecutionBudget()) break; + continue; + } + if (!hasExecutionBudget()) break; + const type = physicalSelectionRecordType(stat); + if (type === "directory") { + if (!hasExecutionBudget()) break; + let realDirectory; + try { + realDirectory = await fs10.realpath(target); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + unstable = true; + addRecord({ path: relative, type }); + omissions.push({ + path: relative, + size: null, + type: "directory", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + await options.hooks?.afterNode?.(relative); + if (stopped) break; + if (!hasExecutionBudget()) break; + continue; + } + if (!hasExecutionBudget()) break; + if (!isInsidePath(options.physicalProjectRoot, realDirectory) || sameOrInside(options.physicalNativeRoot, realDirectory)) { + continue; + } + addRecord({ path: relative, type }); + await options.hooks?.afterNode?.(relative); + if (!hasExecutionBudget()) break; + await visit(target); + if (stopped) break; + continue; + } + addRecord({ path: relative, type }); + await options.hooks?.afterNode?.(relative); + if (!hasExecutionBudget()) break; + if (stopped) break; + } + } catch (error) { + traversalFailed = true; + traversalError = error; + } + let closeError; + try { + await handle.close(); + } catch (error) { + closeError = error; + } + const withinBudget = hasExecutionBudget(); + if (traversalFailed) throw traversalError; + if (withinBudget && closeError !== void 0 && closeError.code !== "ERR_DIR_CLOSED") { + throw closeError; + } + }; + await visit(projectRoot); + hasExecutionBudget(); + if (overflow) { + records.length = 0; + recordCount = 0; + encodedBytes = 0; + } else { + records.sort((left, right) => left.path.localeCompare(right.path, "en")); + omissions.sort((left, right) => left.path.localeCompare(right.path, "en")); + } + const hash6 = overflow ? sha256Text( + `comet.native.physical-selection-incomplete.v1 +${JSON.stringify({ + maxNodes: options.limits.maxNodes, + maxBytes: options.limits.maxBytes, + maxPathBytes: options.limits.maxPathBytes + })}` + ) : sha256Text( + `comet.native.physical-selection.v1 +${JSON.stringify({ + visitedNodeCount, + recordCount, + encodedBytes, + xor: xor.toString("hex"), + sum: sum.toString(16).padStart(64, "0") + })}` + ); + return { + records, + omissions, + evidence: { + hash: hash6, + visitedNodeCount, + recordCount, + storedRecordCount: records.length, + encodedBytes, + overflow, + unstable + } + }; +} +function samePhysicalSelectionStream(left, right) { + return left.hash === right.hash && left.visitedNodeCount === right.visitedNodeCount && left.recordCount === right.recordCount && left.storedRecordCount === right.storedRecordCount && left.encodedBytes === right.encodedBytes && left.overflow === right.overflow && left.unstable === right.unstable; +} +function finalizeNativePhysicalSelection(before, after) { + const hasOverflow = before.overflow || after.overflow; + const changed = before.unstable || after.unstable || !samePhysicalSelectionStream(before, after); + const omissions = []; + if (hasOverflow) { + omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "physical-enumeration-limit" + }); + } + if (changed) { + omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "physical-selection-changed" + }); + } + return { + omissions, + evidence: hasOverflow || changed ? { + schema: "comet.native.physical-selection.v1", + status: hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed", + before, + after + } : null + }; +} +function selectionPaths(selection) { + return [.../* @__PURE__ */ new Set([...selection.tracked, ...selection.untracked])].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +async function readGitlinkWorkingTreeHeadHash(execution, target) { + const output = await runGitBoundedOutput(execution, target, ["rev-parse", "--verify", "HEAD"]); + const objectId = output.toString("utf8").trim().toLowerCase(); + if (!GIT_OBJECT_ID_PATTERN.test(objectId)) { + throw new Error("Native Git snapshot provider received an invalid submodule HEAD"); + } + return sha256Text(`gitlink:${objectId}`); +} +async function inspectGitlinkWorkingTree(execution, target) { + const before = await readGitlinkWorkingTreeHeadHash(execution, target); + const dirty = await runGitHasOutput(execution, target, [ + "status", + "--porcelain=v1", + "-z", + "--untracked-files=normal" + ]); + const after = await readGitlinkWorkingTreeHeadHash(execution, target); + if (after !== before) { + throw new Error("Native Git snapshot provider observed a changing submodule HEAD"); + } + return { hash: after, dirty }; +} +function isSnapshotProjectRef(paths, relative) { + if (nativeSensitiveRelativePathReason(relative) !== null) return false; + const target = path10.resolve(paths.projectRoot, ...relative.split("/")); + return !sameOrInside(path10.resolve(paths.nativeRoot), target); +} +function inspectNativeContentSnapshotHealth(value, options = {}) { + const manifest = parseNativeContentSnapshotManifest(value); + const maxRecordedPaths = options.maxRecordedPaths ?? 20; + if (!Number.isSafeInteger(maxRecordedPaths) || maxRecordedPaths < 0) { + throw new Error("Native snapshot health maxRecordedPaths must be a non-negative integer"); + } + const samplePaths = manifest.omitted.slice(0, maxRecordedPaths).map((omission) => omission.path); + return { + complete: manifest.complete, + omittedCount: manifest.omittedCount, + recordedOmissionCount: manifest.omitted.length, + overflowCount: manifest.omissionOverflow?.count ?? 0, + samplePaths, + sampleTruncated: samplePaths.length < manifest.omittedCount + }; +} +function portableRelative(root, target) { + return path10.relative(root, target).split(path10.sep).join("/"); +} +function normalizedDenylist(projectRoot, values) { + return values.map((value) => path10.resolve(projectRoot, ...value.split(/[\\/]/u))); +} +function sameOrInside(root, target) { + const normalizedRoot = path10.resolve(root); + const normalizedTarget = path10.resolve(target); + return normalizedTarget === normalizedRoot || isInsidePath(normalizedRoot, normalizedTarget); +} +function isUnreadableError(error) { + return UNREADABLE_ERROR_CODES.has(error.code ?? ""); +} +function isChangedDuringReadError(error) { + return error.code === "ENOENT"; +} +function serializedManifestBytes(manifest) { + return Buffer.byteLength(JSON.stringify(manifest, null, 2) + "\n"); +} +function snapshotPolicyHash(include, exclude) { + return sha256Text( + `comet.native.snapshot-policy.v1 +${JSON.stringify({ include, exclude, hash: "sha256" })}` + ); +} +function epsilonClosure(tokens, positions, checkpoint) { + const closure = new Set(positions); + const pending = [...positions]; + while (pending.length > 0) { + if (checkpoint && !checkpoint()) return null; + const position = pending.pop(); + const token = tokens[position]; + if (token && (token.kind === "star" || token.kind === "globstar" || token.kind === "globstar-slash") && !closure.has(position + 1)) { + closure.add(position + 1); + pending.push(position + 1); + } + } + return closure; +} +function cooperativePatternCheckpoint(hasBudget) { + let operationsUntilCheck = 0; + return () => { + if (operationsUntilCheck > 0) { + operationsUntilCheck -= 1; + return true; + } + if (!hasBudget()) return false; + operationsUntilCheck = 63; + return true; + }; +} +function compileNativeSnapshotPattern(pattern) { + const normalized = normalizeNativeSnapshotPattern(pattern, "Native snapshot pattern"); + const tokens = []; + for (let index = 0; index < normalized.length; index += 1) { + const character = normalized[index]; + if (character === "*" && normalized[index + 1] === "*") { + index += 1; + if (normalized[index + 1] === "/") { + index += 1; + tokens.push({ kind: "globstar-slash" }); + } else { + tokens.push({ kind: "globstar" }); + } + } else if (character === "*") { + tokens.push({ kind: "star" }); + } else if (character === "?") { + tokens.push({ kind: "question" }); + } else { + tokens.push({ kind: "literal", value: character }); + } + } + return (relative, hasBudget) => { + const checkpoint = hasBudget ? cooperativePatternCheckpoint(hasBudget) : void 0; + if (checkpoint && !checkpoint()) return false; + let positions = epsilonClosure(tokens, /* @__PURE__ */ new Set([0]), checkpoint); + if (positions === null) return false; + for (const character of relative) { + if (checkpoint && !checkpoint()) return false; + const next = /* @__PURE__ */ new Set(); + for (const position of positions) { + if (checkpoint && !checkpoint()) return false; + const token = tokens[position]; + if (!token) continue; + if (token.kind === "literal" && token.value === character) { + next.add(position + 1); + } else if (token.kind === "question" && character !== "/") { + next.add(position + 1); + } else if (token.kind === "star" && character !== "/") { + next.add(position); + } else if (token.kind === "globstar") { + next.add(position); + } else if (token.kind === "globstar-slash") { + next.add(position); + if (character === "/") next.add(position + 1); + } + } + positions = epsilonClosure(tokens, next, checkpoint); + if (positions === null) return false; + if (positions.size === 0) return false; + } + return epsilonClosure(tokens, positions, checkpoint)?.has(tokens.length) ?? false; + }; +} +function resolveSnapshotPolicy(value) { + if (value === void 0) return void 0; + const include = [ + ...new Set(value.include.map((item) => normalizeNativeSnapshotPattern(item, "include"))) + ].sort((left, right) => left.localeCompare(right, "en")); + const exclude = [ + ...new Set(value.exclude.map((item) => normalizeNativeSnapshotPattern(item, "exclude"))) + ].sort((left, right) => left.localeCompare(right, "en")); + if (include.length === 0) throw new Error("Native snapshot policy include must not be empty"); + const hash6 = snapshotPolicyHash(include, exclude); + if ("hash" in value && value.hash !== hash6) { + throw new Error("Native snapshot policy hash is invalid"); + } + return { + manifest: { + schema: "comet.native.snapshot-policy.v1", + include, + exclude, + hash: hash6 + }, + includeMatchers: include.map(compileNativeSnapshotPattern), + excludeMatchers: exclude.map(compileNativeSnapshotPattern) + }; +} +function snapshotPolicyIncludes(policy, relative, execution) { + if (!policy) return true; + const hasBudget = () => nativeSnapshotExecutionHasBudget(execution); + let included = false; + for (const matcher of policy.includeMatchers) { + if (!hasBudget()) return false; + if (matcher(relative, hasBudget)) { + included = true; + break; + } + } + if (!included) return false; + for (const matcher of policy.excludeMatchers) { + if (!hasBudget()) return false; + if (matcher(relative, hasBudget)) return false; + } + return true; +} +function foldSnapshotOverflowHash(previous, kind, value) { + const payload = JSON.stringify(value); + return sha256Text( + `${previous} +${Buffer.byteLength(kind)}:${kind} +${Buffer.byteLength(payload)}:${payload}` + ); +} +function isSelectionIntegrityOmission(omission) { + return omission.reason === "git-enumeration-limit" || omission.reason === "git-selection-changed" || omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed"; +} +function takeLastCompactableOmission(omissions) { + for (let index = omissions.length - 1; index >= 0; index -= 1) { + const omission = omissions[index]; + if (isSelectionIntegrityOmission(omission)) continue; + omissions.splice(index, 1); + return omission; + } + return null; +} +function sameFileIdentity4(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function sha256FileBounded(file, maxBytes, expected, execution) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + let handle; + try { + handle = await fs10.open(file, "r"); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + throw error; + } + if (!nativeSnapshotExecutionHasBudget(execution)) { + await handle.close(); + return { status: "budget-exhausted" }; + } + const hash6 = createHash5("sha256"); + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + let bytes = 0; + try { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const opened = await handle.stat(); + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + if (!opened.isFile() || !sameFileIdentity4(expected, opened)) return { status: "changed" }; + while (true) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const remaining = maxBytes + 1 - bytes; + if (remaining < 1) return { status: "changed" }; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + if (result2.bytesRead === 0) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const finalStat = await handle.stat(); + if (!nativeSnapshotExecutionHasBudget(execution)) { + return { status: "budget-exhausted" }; + } + if (!finalStat.isFile() || !sameFileIdentity4(opened, finalStat)) { + return { status: "changed" }; + } + return { status: "complete", hash: hash6.digest("hex"), bytes, finalStat }; + } + if (bytes + result2.bytesRead > maxBytes) return { status: "changed" }; + hash6.update(buffer.subarray(0, result2.bytesRead)); + bytes += result2.bytesRead; + } + } finally { + await handle.close(); + } +} +function record3(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function rejectUnknown3(value, keys, label) { + const unknown = Object.keys(value).find((key) => !keys.has(key)); + if (unknown) throw new Error(`${label} contains unknown field: ${unknown}`); +} +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function nonNegativeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function snapshotPath(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + const normalized = path10.posix.normalize(value); + if (normalized !== value || path10.posix.isAbsolute(value) || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function parseEntry(value, index) { + const entry2 = record3(value, `Native snapshot entry ${index}`); + rejectUnknown3(entry2, ENTRY_KEYS, `Native snapshot entry ${index}`); + const entryPath = snapshotPath(entry2.path, `Native snapshot entry ${index} path`); + if (typeof entry2.hash !== "string" || !HASH_PATTERN.test(entry2.hash)) { + throw new Error(`Native snapshot entry ${index} hash is invalid`); + } + if (entry2.type !== "file") throw new Error(`Native snapshot entry ${index} type is invalid`); + return { + path: entryPath, + hash: entry2.hash, + size: nonNegativeInteger(entry2.size, `Native snapshot entry ${index} size`), + type: "file" + }; +} +function parseOmission(value, index) { + const omission = record3(value, `Native snapshot omission ${index}`); + rejectUnknown3(omission, OMISSION_KEYS, `Native snapshot omission ${index}`); + if (!OMISSION_TYPES.has(omission.type)) { + throw new Error(`Native snapshot omission ${index} type is invalid`); + } + if (!OMISSION_REASONS.has(omission.reason)) { + throw new Error(`Native snapshot omission ${index} reason is invalid`); + } + return { + path: snapshotPath(omission.path, `Native snapshot omission ${index} path`), + size: omission.size === null ? null : nonNegativeInteger(omission.size, `Native snapshot omission ${index} size`), + type: omission.type, + reason: omission.reason + }; +} +function parseOmissionOverflow(value) { + const overflow = record3(value, "Native snapshot omission overflow"); + rejectUnknown3(overflow, OMISSION_OVERFLOW_KEYS, "Native snapshot omission overflow"); + if (typeof overflow.hash !== "string" || !HASH_PATTERN.test(overflow.hash)) { + throw new Error("Native snapshot omission overflow hash is invalid"); + } + const expectedRef = `native-snapshot://omitted-overflow/${overflow.hash}`; + if (overflow.ref !== expectedRef) { + throw new Error("Native snapshot omission overflow ref is invalid"); + } + return { + ref: expectedRef, + hash: overflow.hash, + count: positiveInteger(overflow.count, "Native snapshot omission overflow count") + }; +} +function parseGitSelectionStreamEvidence(value, label) { + const stream = record3(value, label); + rejectUnknown3(stream, GIT_SELECTION_STREAM_KEYS, label); + if (typeof stream.hash !== "string" || !HASH_PATTERN.test(stream.hash)) { + throw new Error(`${label} hash is invalid`); + } + if (typeof stream.overflow !== "boolean") { + throw new Error(`${label} overflow flag is invalid`); + } + const recordCount = nonNegativeInteger(stream.recordCount, `${label} recordCount`); + const storedRecordCount = nonNegativeInteger( + stream.storedRecordCount, + `${label} storedRecordCount` + ); + const stdoutBytes = nonNegativeInteger(stream.stdoutBytes, `${label} stdoutBytes`); + if (storedRecordCount > recordCount || !stream.overflow && storedRecordCount !== recordCount) { + throw new Error(`${label} stored record count is inconsistent`); + } + return { + hash: stream.hash, + recordCount, + storedRecordCount, + stdoutBytes, + overflow: stream.overflow + }; +} +function parseGitSelectionEvidence(value) { + const selection = record3(value, "Native Git selection evidence"); + rejectUnknown3(selection, GIT_SELECTION_KEYS, "Native Git selection evidence"); + if (selection.schema !== "comet.native.git-selection.v1") { + throw new Error("Native Git selection evidence schema is invalid"); + } + if (selection.status !== "overflow" && selection.status !== "changed" && selection.status !== "overflow-and-changed") { + throw new Error("Native Git selection evidence status is invalid"); + } + const stageBefore = parseGitSelectionStreamEvidence( + selection.stageBefore, + "Native Git selection stageBefore" + ); + const combined = parseGitSelectionStreamEvidence( + selection.combined, + "Native Git selection combined" + ); + const stageAfter = parseGitSelectionStreamEvidence( + selection.stageAfter, + "Native Git selection stageAfter" + ); + const finalStageBefore = parseGitSelectionStreamEvidence( + selection.finalStageBefore, + "Native Git selection finalStageBefore" + ); + const finalCombined = parseGitSelectionStreamEvidence( + selection.finalCombined, + "Native Git selection finalCombined" + ); + const finalStageAfter = parseGitSelectionStreamEvidence( + selection.finalStageAfter, + "Native Git selection finalStageAfter" + ); + const hasOverflow = [ + stageBefore, + combined, + stageAfter, + finalStageBefore, + finalCombined, + finalStageAfter + ].some((stream) => stream.overflow); + const changed = !sameGitSelectionStream(stageBefore, stageAfter) || !sameGitSelectionStream(stageAfter, finalStageBefore) || !sameGitSelectionStream(combined, finalCombined) || !sameGitSelectionStream(finalStageBefore, finalStageAfter); + const expectedStatus = hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed"; + if (!hasOverflow && !changed) { + throw new Error("Native Git selection evidence must describe an exceptional selection"); + } + if (selection.status !== expectedStatus) { + throw new Error("Native Git selection evidence status is inconsistent"); + } + return { + schema: "comet.native.git-selection.v1", + status: expectedStatus, + stageBefore, + combined, + stageAfter, + finalStageBefore, + finalCombined, + finalStageAfter + }; +} +function parsePhysicalSelectionStreamEvidence(value, label) { + const stream = record3(value, label); + rejectUnknown3(stream, PHYSICAL_SELECTION_STREAM_KEYS, label); + if (typeof stream.hash !== "string" || !HASH_PATTERN.test(stream.hash)) { + throw new Error(`${label} hash is invalid`); + } + if (typeof stream.overflow !== "boolean" || typeof stream.unstable !== "boolean") { + throw new Error(`${label} flags are invalid`); + } + const visitedNodeCount = nonNegativeInteger(stream.visitedNodeCount, `${label} visitedNodeCount`); + const recordCount = nonNegativeInteger(stream.recordCount, `${label} recordCount`); + const storedRecordCount = nonNegativeInteger( + stream.storedRecordCount, + `${label} storedRecordCount` + ); + const encodedBytes = nonNegativeInteger(stream.encodedBytes, `${label} encodedBytes`); + if (storedRecordCount > recordCount || !stream.overflow && storedRecordCount !== recordCount) { + throw new Error(`${label} stored record count is inconsistent`); + } + return { + hash: stream.hash, + visitedNodeCount, + recordCount, + storedRecordCount, + encodedBytes, + overflow: stream.overflow, + unstable: stream.unstable + }; +} +function parsePhysicalSelectionEvidence(value) { + const selection = record3(value, "Native physical selection evidence"); + rejectUnknown3(selection, PHYSICAL_SELECTION_KEYS, "Native physical selection evidence"); + if (selection.schema !== "comet.native.physical-selection.v1") { + throw new Error("Native physical selection evidence schema is invalid"); + } + if (selection.status !== "overflow" && selection.status !== "changed" && selection.status !== "overflow-and-changed") { + throw new Error("Native physical selection evidence status is invalid"); + } + const before = parsePhysicalSelectionStreamEvidence( + selection.before, + "Native physical selection before" + ); + const after = parsePhysicalSelectionStreamEvidence( + selection.after, + "Native physical selection after" + ); + const hasOverflow = before.overflow || after.overflow; + const changed = before.unstable || after.unstable || !samePhysicalSelectionStream(before, after); + if (!hasOverflow && !changed) { + throw new Error("Native physical selection evidence must describe an exceptional selection"); + } + const expectedStatus = hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed"; + if (selection.status !== expectedStatus) { + throw new Error("Native physical selection evidence status is inconsistent"); + } + return { + schema: "comet.native.physical-selection.v1", + status: expectedStatus, + before, + after + }; +} +function parseNativeContentSnapshotManifest(value) { + const manifest = record3(value, "Native content snapshot manifest"); + rejectUnknown3(manifest, MANIFEST_KEYS, "Native content snapshot manifest"); + if (manifest.schema !== "comet.native.content-snapshot.v1") { + throw new Error("Unsupported Native content snapshot schema"); + } + if (!SNAPSHOT_ORIGINS.has(manifest.origin)) { + throw new Error("Native content snapshot origin is invalid"); + } + let capture; + if (manifest.capture !== void 0) { + const captureValue = record3(manifest.capture, "Native content snapshot capture"); + rejectUnknown3(captureValue, CAPTURE_KEYS, "Native content snapshot capture"); + if (captureValue.provider !== "git" && captureValue.provider !== "physical-tree") { + throw new Error("Native content snapshot capture provider is invalid"); + } + const gitSelection2 = captureValue.gitSelection === void 0 ? void 0 : parseGitSelectionEvidence(captureValue.gitSelection); + const physicalSelection2 = captureValue.physicalSelection === void 0 ? void 0 : parsePhysicalSelectionEvidence(captureValue.physicalSelection); + let projection = null; + if (captureValue.projection !== void 0) { + const projectionValue = record3(captureValue.projection, "Native content snapshot projection"); + rejectUnknown3(projectionValue, GIT_PROJECTION_KEYS, "Native content snapshot projection"); + if (projectionValue.provider !== "git") { + throw new Error("Native content snapshot projection provider is invalid"); + } + projection = { + provider: "git", + ...projectionValue.selection === void 0 ? {} : { selection: parseGitSelectionEvidence(projectionValue.selection) } + }; + } + if (captureValue.provider === "git") { + if (physicalSelection2 || projection) { + throw new Error("Native Git capture cannot include physical or projection evidence"); + } + capture = { + provider: "git", + ...gitSelection2 ? { gitSelection: gitSelection2 } : {} + }; + } else { + if (gitSelection2) { + throw new Error("Native physical-tree capture cannot include direct Git evidence"); + } + if (physicalSelection2 && projection) { + throw new Error("Native physical-tree capture cannot combine selection and projection"); + } + capture = projection ? { provider: "physical-tree", projection } : { + provider: "physical-tree", + ...physicalSelection2 ? { physicalSelection: physicalSelection2 } : {} + }; + } + } + if (typeof manifest.createdAt !== "string" || Number.isNaN(Date.parse(manifest.createdAt))) { + throw new Error("Native content snapshot timestamp is invalid"); + } + if (typeof manifest.complete !== "boolean") { + throw new Error("Native content snapshot complete flag is invalid"); + } + const limitValue = record3(manifest.limits, "Native content snapshot limits"); + rejectUnknown3(limitValue, LIMIT_KEYS, "Native content snapshot limits"); + const limits = { + maxFiles: positiveInteger(limitValue.maxFiles, "Native snapshot maxFiles"), + maxFileBytes: positiveInteger(limitValue.maxFileBytes, "Native snapshot maxFileBytes"), + maxTotalBytes: positiveInteger(limitValue.maxTotalBytes, "Native snapshot maxTotalBytes"), + maxManifestBytes: positiveInteger( + limitValue.maxManifestBytes, + "Native snapshot maxManifestBytes" + ), + ...limitValue.maxDurationMs === void 0 ? {} : { + maxDurationMs: positiveInteger(limitValue.maxDurationMs, "Native snapshot maxDurationMs") + } + }; + let policy; + if (manifest.policy !== void 0) { + const policyValue = record3(manifest.policy, "Native snapshot policy"); + rejectUnknown3(policyValue, POLICY_KEYS, "Native snapshot policy"); + if (policyValue.schema !== "comet.native.snapshot-policy.v1") { + throw new Error("Native snapshot policy schema is invalid"); + } + if (!Array.isArray(policyValue.include) || !Array.isArray(policyValue.exclude)) { + throw new Error("Native snapshot policy patterns must be arrays"); + } + policy = resolveSnapshotPolicy({ + include: policyValue.include, + exclude: policyValue.exclude, + hash: policyValue.hash, + schema: "comet.native.snapshot-policy.v1" + }).manifest; + } + if (!Array.isArray(manifest.entries) || !Array.isArray(manifest.omitted)) { + throw new Error("Native content snapshot entries and omissions must be arrays"); + } + const entries = manifest.entries.map(parseEntry); + const omitted = manifest.omitted.map(parseOmission); + const omittedCount = nonNegativeInteger( + manifest.omittedCount, + "Native content snapshot omittedCount" + ); + const omissionOverflow = manifest.omissionOverflow === void 0 ? void 0 : parseOmissionOverflow(manifest.omissionOverflow); + if (entries.length > limits.maxFiles) { + throw new Error("Native content snapshot exceeds its file-count limit"); + } + if (entries.some((entry2) => entry2.size > limits.maxFileBytes) || entries.reduce((total, entry2) => total + entry2.size, 0) > limits.maxTotalBytes) { + throw new Error("Native content snapshot exceeds its byte limits"); + } + if (new Set(entries.map((entry2) => entry2.path)).size !== entries.length) { + throw new Error("Native content snapshot contains duplicate paths"); + } + if (omitted.length > MAX_RECORDED_OMISSIONS || omittedCount < omitted.length) { + throw new Error("Native content snapshot omission count is invalid"); + } + const overflowCount = omittedCount - omitted.length; + if (overflowCount === 0 && omissionOverflow || overflowCount > 0 && omissionOverflow?.count !== overflowCount) { + throw new Error("Native content snapshot omission overflow is inconsistent"); + } + if (manifest.complete !== (omittedCount === 0)) { + throw new Error("Native content snapshot completeness is inconsistent"); + } + const enumerationOmissions = omitted.filter( + (omission) => omission.reason === "git-enumeration-limit" + ); + const selectionChangedOmissions = omitted.filter( + (omission) => omission.reason === "git-selection-changed" + ); + for (const omission of [...enumerationOmissions, ...selectionChangedOmissions]) { + if (omission.path !== "." || omission.size !== null || omission.type !== "directory") { + throw new Error("Native Git selection omission must use the project-root sentinel"); + } + } + if (enumerationOmissions.length > 1 || selectionChangedOmissions.length > 1) { + throw new Error("Native Git selection omissions must not be duplicated"); + } + const gitSelection = capture?.provider === "git" ? capture.gitSelection : capture?.projection?.selection; + const evidenceHasOverflow = gitSelection?.status === "overflow" || gitSelection?.status === "overflow-and-changed"; + const evidenceHasSelectionChange = gitSelection?.status === "changed" || gitSelection?.status === "overflow-and-changed"; + if (evidenceHasOverflow !== (enumerationOmissions.length === 1)) { + throw new Error("Native Git enumeration omission and selection evidence are inconsistent"); + } + if (evidenceHasSelectionChange !== (selectionChangedOmissions.length === 1)) { + throw new Error("Native Git selection-change omission and selection evidence are inconsistent"); + } + const physicalEnumerationOmissions = omitted.filter( + (omission) => omission.reason === "physical-enumeration-limit" + ); + const physicalChangedOmissions = omitted.filter( + (omission) => omission.reason === "physical-selection-changed" + ); + for (const omission of [...physicalEnumerationOmissions, ...physicalChangedOmissions]) { + if (omission.path !== "." || omission.size !== null || omission.type !== "directory") { + throw new Error("Native physical selection omission must use the project-root sentinel"); + } + } + if (physicalEnumerationOmissions.length > 1 || physicalChangedOmissions.length > 1) { + throw new Error("Native physical selection omissions must not be duplicated"); + } + const physicalSelection = capture?.physicalSelection; + const physicalEvidenceHasOverflow = physicalSelection?.status === "overflow" || physicalSelection?.status === "overflow-and-changed"; + const physicalEvidenceHasChange = physicalSelection?.status === "changed" || physicalSelection?.status === "overflow-and-changed"; + if (physicalEvidenceHasOverflow !== (physicalEnumerationOmissions.length === 1)) { + throw new Error("Native physical enumeration omission and evidence are inconsistent"); + } + if (physicalEvidenceHasChange !== (physicalChangedOmissions.length === 1)) { + throw new Error("Native physical selection-change omission and evidence are inconsistent"); + } + const parsed = { + schema: "comet.native.content-snapshot.v1", + origin: manifest.origin, + ...capture ? { capture } : {}, + createdAt: manifest.createdAt, + complete: manifest.complete, + limits, + ...policy ? { policy } : {}, + entries, + omitted, + omittedCount, + ...omissionOverflow ? { omissionOverflow } : {} + }; + if (serializedManifestBytes(parsed) > limits.maxManifestBytes) { + throw new Error("Native content snapshot exceeds its manifest byte limit"); + } + return parsed; +} +async function filterNativeContentSnapshotToProjectScope(paths, value, options = {}) { + const manifest = parseNativeContentSnapshotManifest(value); + if (manifest.capture?.provider === "git" || manifest.capture?.projection || manifest.capture?.physicalSelection) { + return manifest; + } + const projectRoot = path10.resolve(paths.projectRoot); + const execution = createNativeSnapshotExecution(options); + const gitSelectionLimits = resolveNativeGitSelectionLimits(options.gitSelectionLimits); + const selection = await nativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + options.gitSelectionHooks + ); + if (selection === null) return manifest; + const selected = new Set( + selectionPaths(selection).filter((relative) => isSnapshotProjectRef(paths, relative)) + ); + const gitlinkPaths = [...selection.gitlinks].filter((relative) => selected.has(relative)); + const nestedRepositoryPaths = [...selection.nestedRepositories].filter( + (relative) => isSnapshotProjectRef(paths, relative) + ); + const atOrBeneath = (relative, boundaries) => boundaries.some((boundary) => relative === boundary || relative.startsWith(`${boundary}/`)); + const beneathGitlink = (relative) => atOrBeneath(relative, gitlinkPaths); + const beneathNestedRepository = (relative) => atOrBeneath(relative, nestedRepositoryPaths); + const ignoredCandidates = [ + ...manifest.entries.map((entry2) => entry2.path), + ...manifest.omitted.flatMap( + (omission) => omission.type === "directory" ? [omission.path, `${omission.path}/`] : [omission.path] + ) + ].filter( + (relative) => !selected.has(relative) && !beneathGitlink(relative) && !beneathNestedRepository(relative) + ); + const ignored = await runGitCheckIgnore(execution, projectRoot, ignoredCandidates); + const entries = manifest.entries.filter( + (entry2) => !beneathGitlink(entry2.path) && !beneathNestedRepository(entry2.path) && isSnapshotProjectRef(paths, entry2.path) && (selected.has(entry2.path) || !ignored.has(entry2.path)) + ); + await finalizeNativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + selection, + options.gitSelectionHooks?.outputChunkBytes + ); + const boundaryOmissions = [...selection.omissions]; + for (const gitlink of gitlinkPaths) { + boundaryOmissions.push({ + path: gitlink, + size: null, + type: "directory", + reason: "legacy-gitlink-boundary" + }); + } + entries.sort((left, right) => left.path.localeCompare(right.path, "en")); + const omitted = manifest.omitted.filter((omission) => { + if (beneathGitlink(omission.path)) return false; + if (beneathNestedRepository(omission.path)) return false; + if (!isSnapshotProjectRef(paths, omission.path)) return false; + if (!ignored.has(omission.path)) return true; + return selected.has(omission.path) || [...selected].some((relative) => relative.startsWith(`${omission.path}/`)); + }); + for (const omission of boundaryOmissions) { + if (!omitted.some( + (current) => current.path === omission.path && current.reason === omission.reason + )) { + omitted.push(omission); + } + } + omitted.sort((left, right) => left.path.localeCompare(right.path, "en")); + let overflowCount = manifest.omissionOverflow?.count ?? 0; + let overflowHash = manifest.omissionOverflow?.hash ?? sha256Text("comet.native.snapshot-omission-overflow.v1"); + if (selection.overflow) { + overflowCount += selection.overflow.count; + overflowHash = foldSnapshotOverflowHash(overflowHash, "git-selection", { + source: "git-selection", + ...selection.overflow + }); + } + const foldOverflow = (omission) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "omission", omission); + }; + while (omitted.length > MAX_RECORDED_OMISSIONS) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) { + throw new Error( + "Projected Native snapshot has too many required selection-integrity omissions" + ); + } + foldOverflow(omission); + } + const buildProjection = () => ({ + ...manifest, + capture: { + provider: "physical-tree", + projection: { + provider: "git", + ...selection.evidence ? { selection: selection.evidence } : {} + } + }, + complete: omitted.length + overflowCount === 0, + entries, + omitted, + omittedCount: omitted.length + overflowCount, + ...overflowCount > 0 ? { + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${overflowHash}`, + hash: overflowHash, + count: overflowCount + } + } : {} + }); + let projected = buildProjection(); + while (serializedManifestBytes(projected) > manifest.limits.maxManifestBytes) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) { + throw new Error("Projected Native snapshot cannot fit its manifest byte limit"); + } + foldOverflow(omission); + projected = buildProjection(); + } + return parseNativeContentSnapshotManifest(projected); +} +function nativeBaselineManifestFile(paths, name) { + if (!CHANGE_NAME_PATTERN.test(name)) throw new Error(`Invalid Native change name: ${name}`); + const changeDir = path10.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, changeDir)) throw new Error("Native change path escaped"); + return path10.join(changeDir, "runtime", "baseline-manifest.json"); +} +async function createNativeContentSnapshot(paths, options = {}) { + const limits = { + maxFiles: options.limits?.maxFiles ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxFiles, + maxFileBytes: options.limits?.maxFileBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxFileBytes, + maxTotalBytes: options.limits?.maxTotalBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxTotalBytes, + maxManifestBytes: options.limits?.maxManifestBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxManifestBytes, + ...options.limits?.maxDurationMs === void 0 ? {} : { maxDurationMs: options.limits.maxDurationMs } + }; + const policy = resolveSnapshotPolicy(options.policy); + const execution = createNativeSnapshotExecution({ + ...options, + deadlineMs: options.deadlineMs ?? limits.maxDurationMs + }); + const gitSelectionLimits = resolveNativeGitSelectionLimits(options.gitSelectionLimits); + const physicalSelectionLimits = resolveNativePhysicalSelectionLimits( + options.physicalSelectionLimits + ); + if (limits.maxFiles < 1 || limits.maxFileBytes < 1 || limits.maxTotalBytes < 1 || limits.maxManifestBytes < 1) { + throw new Error("Native snapshot limits must be positive"); + } + const projectRoot = path10.resolve(paths.projectRoot); + const physicalProjectRoot = await fs10.realpath(projectRoot); + const nativeRoot = path10.resolve(paths.nativeRoot); + const physicalNativeRoot = await fs10.realpath(nativeRoot); + const configFile = path10.resolve(paths.configFile); + const selectionFile = path10.join(projectRoot, ".comet", "current-change.json"); + const denylist = normalizedDenylist(projectRoot, options.denylist ?? []); + const entries = []; + const omitted = []; + const capturedEntryValidations = /* @__PURE__ */ new Map(); + const capturedTrackedAbsences = /* @__PURE__ */ new Map(); + let omittedCount = 0; + let overflowCount = 0; + let overflowHash = sha256Text("comet.native.snapshot-omission-overflow.v1"); + let totalBytes = 0; + let notifiedFirstEntry = false; + let physicalSelectionEvidence = null; + const foldOverflow = (value) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "omission", value); + }; + const omit = (value) => { + omittedCount += 1; + if (omitted.length < MAX_RECORDED_OMISSIONS) { + omitted.push(value); + return; + } + if (isSelectionIntegrityOmission(value)) { + const displaced = takeLastCompactableOmission(omitted); + if (displaced === null) { + throw new Error("Native snapshot has too many required selection-integrity omissions"); + } + foldOverflow(displaced); + omitted.push(value); + return; + } + foldOverflow(value); + }; + const foldGitSelectionOverflow = (value) => { + omittedCount += value.count; + overflowCount += value.count; + overflowHash = foldSnapshotOverflowHash(overflowHash, "git-selection", { + source: "git-selection", + ...value + }); + }; + const foldManifestEntryOverflow = (entry2) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "manifest-entry", { + reason: "manifest-size", + entry: entry2 + }); + }; + const recordCapturedEntry = async (entry2, validation2) => { + entries.push(entry2); + totalBytes += entry2.size; + capturedEntryValidations.set(entry2.path, validation2); + if (!notifiedFirstEntry) { + notifiedFirstEntry = true; + await options.gitSelectionHooks?.afterFirstEntryCaptured?.(entry2.path); + } + }; + const invalidateCapturedEntry = (relative, omission) => { + const index = entries.findIndex((entry3) => entry3.path === relative); + if (index < 0) return; + const [entry2] = entries.splice(index, 1); + totalBytes -= entry2.size; + capturedEntryValidations.delete(relative); + omit(omission); + }; + const captureFile = async (target, relative, before) => { + if (!before.isFile() || before.isSymbolicLink()) return; + if (!nativeSnapshotExecutionHasBudget(execution)) return; + let realTarget; + try { + realTarget = await fs10.realpath(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: before.size, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isInsidePath(physicalProjectRoot, realTarget) || sameOrInside(physicalNativeRoot, realTarget)) { + return; + } + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size: before.size, type: "file", reason: "file-count" }); + return; + } + if (before.size > limits.maxFileBytes) { + omit({ path: relative, size: before.size, type: "file", reason: "file-size" }); + return; + } + if (totalBytes + before.size > limits.maxTotalBytes) { + omit({ path: relative, size: before.size, type: "file", reason: "total-size" }); + return; + } + let boundedHash; + let after; + let afterRealTarget; + try { + boundedHash = await sha256FileBounded(realTarget, before.size, before, execution); + if (boundedHash.status === "budget-exhausted" || !nativeSnapshotExecutionHasBudget(execution)) { + return; + } + if (boundedHash.status === "changed") { + omit({ path: relative, size: null, type: "file", reason: "changed-during-read" }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + afterRealTarget = await fs10.realpath(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + after = await fs10.lstat(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: before.size, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (boundedHash.bytes !== before.size || afterRealTarget !== realTarget || !sameFileIdentity4(before, boundedHash.finalStat) || !sameFileIdentity4(boundedHash.finalStat, after) || !after.isFile() || after.isSymbolicLink() || after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) { + omit({ + path: relative, + size: after.isFile() ? after.size : null, + type: after.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + await recordCapturedEntry( + { path: relative, hash: boundedHash.hash, size: after.size, type: "file" }, + { kind: "file", target, realTarget, stat: after } + ); + }; + const captureSymbolicLink = async (target, relative, before) => { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + let firstTarget; + let secondTarget; + let after; + try { + firstTarget = await fs10.readlink(target, { encoding: "buffer" }); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + after = await fs10.lstat(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + secondTarget = await fs10.readlink(target, { encoding: "buffer" }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: null, + type: "other", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!after.isSymbolicLink() || !sameFileIdentity4(before, after) || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs || !firstTarget.equals(secondTarget)) { + omit({ path: relative, size: null, type: "other", reason: "changed-during-read" }); + return; + } + const size = firstTarget.byteLength; + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size, type: "other", reason: "file-count" }); + return; + } + if (size > limits.maxFileBytes) { + omit({ path: relative, size, type: "other", reason: "file-size" }); + return; + } + if (totalBytes + size > limits.maxTotalBytes) { + omit({ path: relative, size, type: "other", reason: "total-size" }); + return; + } + const hash6 = createHash5("sha256").update("symlink\0").update(firstTarget).digest("hex"); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + await recordCapturedEntry( + { path: relative, hash: hash6, size, type: "file" }, + { kind: "symlink", target, rawTarget: firstTarget, stat: after } + ); + }; + const revalidateCapturedEntries = async () => { + for (const [relative, validation2] of [...capturedEntryValidations]) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (validation2.kind === "file") { + let realTarget2; + let stat2; + try { + realTarget2 = await fs10.realpath(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + stat2 = await fs10.lstat(validation2.target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "file", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (realTarget2 !== validation2.realTarget || !stat2.isFile() || stat2.isSymbolicLink() || !sameFileIdentity4(validation2.stat, stat2) || stat2.size !== validation2.stat.size || stat2.mtimeMs !== validation2.stat.mtimeMs || stat2.ctimeMs !== validation2.stat.ctimeMs) { + invalidateCapturedEntry(relative, { + path: relative, + size: stat2.isFile() ? stat2.size : null, + type: stat2.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + } + continue; + } + if (validation2.kind === "symlink") { + let stat2; + let rawTarget; + try { + stat2 = await fs10.lstat(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + rawTarget = await fs10.readlink(validation2.target, { encoding: "buffer" }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "other", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!stat2.isSymbolicLink() || !sameFileIdentity4(validation2.stat, stat2) || stat2.mtimeMs !== validation2.stat.mtimeMs || stat2.ctimeMs !== validation2.stat.ctimeMs || !validation2.rawTarget.equals(rawTarget)) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "other", + reason: "changed-during-read" + }); + } + continue; + } + let realTarget; + let stat; + try { + realTarget = await fs10.realpath(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + stat = await fs10.lstat(validation2.target); + } catch { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + const validBoundary = realTarget === validation2.realTarget && stat.isDirectory() && !stat.isSymbolicLink() && sameFileIdentity4(validation2.stat, stat) && isInsidePath(physicalProjectRoot, realTarget) && !sameOrInside(physicalNativeRoot, realTarget); + if (!validBoundary) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-changed" + }); + continue; + } + let workingTree; + try { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + workingTree = await inspectGitlinkWorkingTree(execution, realTarget); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (workingTree.dirty) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-dirty" + }); + } else if (workingTree.hash !== validation2.hash) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-changed" + }); + } + } + for (const [relative, target] of capturedTrackedAbsences) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + try { + const stat = await fs10.lstat(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + omit({ + path: relative, + size: stat.isFile() ? stat.size : null, + type: stat.isFile() ? "file" : stat.isDirectory() ? "directory" : "other", + reason: "changed-during-read" + }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (isChangedDuringReadError(error)) continue; + if (!isUnreadableError(error)) throw error; + omit({ path: relative, size: null, type: "file", reason: "unreadable" }); + } + } + }; + const gitSelection = await nativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + options.gitSelectionHooks + ); + if (gitSelection === null) { + const before = await nativePhysicalSnapshotSelection({ + execution, + paths, + physicalProjectRoot, + physicalNativeRoot, + denylist, + limits: physicalSelectionLimits, + hooks: options.physicalSelectionHooks + }); + await options.physicalSelectionHooks?.afterInitialSelection?.(); + for (const record8 of before.records) { + if (record8.type !== "file" && record8.type !== "symlink") continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + if (!snapshotPolicyIncludes(policy, record8.path, execution)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + const target = path10.resolve(projectRoot, ...record8.path.split("/")); + let stat; + try { + stat = await fs10.lstat(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: record8.path, + size: null, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + continue; + } + if (remainingNativeSnapshotTime(execution) < 1) break; + if (record8.type === "symlink" && stat.isSymbolicLink()) { + await captureSymbolicLink(target, record8.path, stat); + if (remainingNativeSnapshotTime(execution) < 1) break; + continue; + } + if (record8.type !== "file" || !stat.isFile() || stat.isSymbolicLink()) { + omit({ + path: record8.path, + size: stat.isFile() ? stat.size : null, + type: stat.isDirectory() ? "directory" : stat.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + continue; + } + await captureFile(target, record8.path, stat); + if (remainingNativeSnapshotTime(execution) < 1) break; + } + if (remainingNativeSnapshotTime(execution) >= 1) await revalidateCapturedEntries(); + const after = await nativePhysicalSnapshotSelection({ + execution, + paths, + physicalProjectRoot, + physicalNativeRoot, + denylist, + limits: physicalSelectionLimits + }); + const finalized = finalizeNativePhysicalSelection(before.evidence, after.evidence); + physicalSelectionEvidence = finalized.evidence; + for (const omission of [...before.omissions, ...after.omissions, ...finalized.omissions]) { + if (omitted.some( + (current) => current.path === omission.path && current.reason === omission.reason + )) { + continue; + } + omit(omission); + } + } else { + await options.gitSelectionHooks?.afterInitialSelection?.(); + for (const relative of selectionPaths(gitSelection)) { + if (!isSnapshotProjectRef(paths, relative)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + if (!snapshotPolicyIncludes(policy, relative, execution)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + const target = path10.resolve(projectRoot, ...relative.split("/")); + if (target === configFile || target === selectionFile || denylist.some((denied) => sameOrInside(denied, target))) { + continue; + } + if (gitSelection.gitlinks.has(relative)) { + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size: 0, type: "file", reason: "file-count" }); + continue; + } + let realGitlink; + let gitlinkStat; + try { + [realGitlink, gitlinkStat] = await Promise.all([fs10.realpath(target), fs10.lstat(target)]); + } catch { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (gitlinkStat.isSymbolicLink() || !gitlinkStat.isDirectory() || !isInsidePath(physicalProjectRoot, realGitlink) || sameOrInside(physicalNativeRoot, realGitlink)) { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + try { + const workingTree = await inspectGitlinkWorkingTree(execution, realGitlink); + if (workingTree.dirty) { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-dirty" + }); + continue; + } + await recordCapturedEntry( + { + path: relative, + hash: workingTree.hash, + size: 0, + type: "file" + }, + { + kind: "gitlink", + target, + realTarget: realGitlink, + stat: gitlinkStat, + hash: workingTree.hash + } + ); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + } + continue; + } + let before; + try { + before = await fs10.lstat(target); + } catch (error) { + if (isChangedDuringReadError(error) && gitSelection.tracked.has(relative)) { + capturedTrackedAbsences.set(relative, target); + continue; + } + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: null, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + continue; + } + if (before.isSymbolicLink()) { + await captureSymbolicLink(target, relative, before); + continue; + } + if (!before.isFile()) { + if (!gitSelection.nestedRepositories.has(relative)) { + omit({ path: relative, size: null, type: "other", reason: "changed-during-read" }); + } + continue; + } + await captureFile(target, relative, before); + } + await revalidateCapturedEntries(); + await finalizeNativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + gitSelection, + options.gitSelectionHooks?.outputChunkBytes + ); + for (const omission of gitSelection.omissions) omit(omission); + if (gitSelection.overflow) foldGitSelectionOverflow(gitSelection.overflow); + } + entries.sort((left, right) => left.path.localeCompare(right.path, "en")); + omitted.sort((left, right) => left.path.localeCompare(right.path, "en")); + const capture = gitSelection === null ? { + provider: "physical-tree", + ...physicalSelectionEvidence ? { physicalSelection: physicalSelectionEvidence } : {} + } : { + provider: "git", + ...gitSelection.evidence ? { gitSelection: gitSelection.evidence } : {} + }; + const buildManifest = () => ({ + schema: "comet.native.content-snapshot.v1", + origin: options.origin ?? "explicit", + capture, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + complete: omittedCount === 0, + limits, + ...policy ? { policy: policy.manifest } : {}, + entries, + omitted, + omittedCount, + ...overflowCount > 0 ? { + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${overflowHash}`, + hash: overflowHash, + count: overflowCount + } + } : {} + }); + let manifest = buildManifest(); + while (serializedManifestBytes(manifest) > limits.maxManifestBytes) { + const compactableOmissionCount = omitted.filter( + (omission) => !isSelectionIntegrityOmission(omission) + ).length; + if (compactableOmissionCount > 0) { + const removeCount = Math.max(1, Math.ceil(omitted.length / 4)); + for (let removed = 0; removed < removeCount; removed += 1) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) break; + foldOverflow(omission); + } + } else if (entries.length > 0) { + const removeCount = Math.max(1, Math.ceil(entries.length / 4)); + for (const entry2 of entries.splice(-removeCount)) { + omittedCount += 1; + foldManifestEntryOverflow(entry2); + } + } else { + throw new Error("Native snapshot manifest byte limit is too small for its metadata"); + } + manifest = buildManifest(); + } + return manifest; +} +async function createNativeCurrentContentSnapshot(paths, baseline, options = {}) { + const config = await readProjectConfig(paths.projectRoot); + const settings = config?.native.snapshot ?? DEFAULT_NATIVE_SNAPSHOT_CONFIG; + return createNativeContentSnapshot(paths, { + ...options, + policy: baseline.policy, + limits: { + maxFiles: settings.max_files, + maxFileBytes: settings.max_total_bytes, + maxTotalBytes: settings.max_total_bytes, + maxDurationMs: settings.max_duration_ms + }, + deadlineMs: settings.max_duration_ms + }); +} +async function writeNativeBaselineManifest(paths, name, manifest) { + const file = nativeBaselineManifestFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + await atomicWriteJson(file, parseNativeContentSnapshotManifest(manifest)); +} +async function readNativeBaselineManifest(paths, name) { + const file = nativeBaselineManifestFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const source = await readNativeProtectedTextFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES, + label: "Native baseline snapshot manifest" + }); + return parseNativeContentSnapshotManifest(JSON.parse(source.text)); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} + +// domains/comet-native/native-trajectory-recovery.ts +import { createHash as createHash7 } from "crypto"; +import path14 from "path"; + +// domains/engine/storage-layout.ts +import path11 from "path"; +var NATIVE_RUN_STORAGE = /* @__PURE__ */ Object.freeze({ + stateRef: "runtime/run-state.json", + pendingRef: "runtime/pending-action.json", + trajectoryRef: "runtime/trajectory.jsonl", + contextRef: "runtime/context.md", + artifactsRef: "runtime/artifacts.json", + checkpointRef: "runtime/checkpoints/latest.json", + snapshotsRef: "runtime/skill-snapshots" +}); +function assertRunStorageRef(value) { + if (value.length === 0 || path11.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error("Run storage ref must stay inside the Run root"); + } +} +function assertRunStorageLayout(storage) { + for (const value of Object.values(storage)) assertRunStorageRef(value); +} + +// domains/comet-native/native-run-store.ts +import { createHash as createHash6 } from "node:crypto"; +import { constants as fsConstants3, promises as fs11 } from "node:fs"; +import path13 from "node:path"; +import { TextDecoder as TextDecoder4 } from "node:util"; + +// domains/engine/state.ts +import path12 from "path"; +var field = (doc, key) => { + const value = doc[key]; + return value === null || value === void 0 ? null : String(value); +}; +function requiredString(doc, key) { + const value = doc[key]; + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Invalid Run state: ${key} must be a non-empty string`); + } + return value; +} +function requiredRunReference(doc, key) { + const value = requiredString(doc, key); + if (path12.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Run state: ${key} must stay inside the change directory`); + } + return value; +} +function retries(doc) { + const raw = doc.run_retries ?? "{}"; + let value; + try { + value = typeof raw === "string" ? JSON.parse(raw) : raw; + } catch (error) { + throw new Error("Invalid Run state: run_retries must be a JSON object", { cause: error }); + } + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: run_retries must be a JSON object"); + } + for (const count of Object.values(value)) { + if (!Number.isInteger(count) || Number(count) < 0) { + throw new Error("Invalid Run state: retry counts must be non-negative integers"); + } + } + return value; +} +function runStateFromDocument(doc) { + if (!doc.run_id) return null; + const runId = requiredString(doc, "run_id"); + const skill = requiredString(doc, "skill"); + const skillVersion = requiredString(doc, "skill_version"); + const skillHash = requiredString(doc, "skill_hash"); + const pendingRef = requiredRunReference(doc, "pending_ref"); + const trajectoryRef = requiredRunReference(doc, "trajectory_ref"); + const contextRef = requiredRunReference(doc, "context_ref"); + const artifactsRef = requiredRunReference(doc, "artifacts_ref"); + const checkpointRef = requiredRunReference(doc, "checkpoint_ref"); + const iteration = Number(doc.iteration); + if (!Number.isInteger(iteration) || iteration < 0) { + throw new Error("Invalid Run state: iteration must be a non-negative integer"); + } + if (doc.orchestration !== "deterministic" && doc.orchestration !== "adaptive") { + throw new Error("Invalid Run state: orchestration must be deterministic or adaptive"); + } + if (doc.run_status !== "running" && doc.run_status !== "waiting" && doc.run_status !== "completed" && doc.run_status !== "failed") { + throw new Error("Invalid Run state: run_status is invalid"); + } + return { + runId, + skill, + skillVersion, + skillHash, + orchestration: doc.orchestration, + currentStep: field(doc, "current_step"), + iteration, + pending: field(doc, "pending"), + pendingRef, + trajectoryRef, + contextRef, + artifactsRef, + checkpointRef, + status: doc.run_status, + retries: retries(doc) + }; +} + +// domains/engine/storage-run.ts +var STORED_RUN_STATE_KEYS = /* @__PURE__ */ new Set([ + "runId", + "skill", + "skillVersion", + "skillHash", + "orchestration", + "currentStep", + "iteration", + "pending", + "pendingRef", + "trajectoryRef", + "contextRef", + "artifactsRef", + "checkpointRef", + "status", + "retries" +]); +function fromStoredState(json) { + const document = { + run_id: json.runId, + skill: json.skill, + skill_version: json.skillVersion, + skill_hash: json.skillHash, + orchestration: json.orchestration, + current_step: json.currentStep, + iteration: json.iteration, + pending: json.pending, + pending_ref: json.pendingRef, + trajectory_ref: json.trajectoryRef, + context_ref: json.contextRef, + artifacts_ref: json.artifactsRef, + checkpoint_ref: json.checkpointRef, + run_status: json.status, + run_retries: JSON.stringify(json.retries) + }; + const parsed = runStateFromDocument(document); + if (!parsed) throw new Error("Invalid Run state: runId must be a non-empty string"); + return parsed; +} +function parseStoredRunStateValue(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: stored value must be an object"); + } + const json = value; + const unknown = Object.keys(json).find( + (key) => !STORED_RUN_STATE_KEYS.has(key) + ); + if (unknown) throw new Error(`Invalid Run state: unknown field ${unknown}`); + if (json.currentStep !== null && (typeof json.currentStep !== "string" || json.currentStep.length === 0)) { + throw new Error("Invalid Run state: currentStep must be a non-empty string or null"); + } + if (json.pending !== null && (typeof json.pending !== "string" || json.pending.length === 0)) { + throw new Error("Invalid Run state: pending must be a non-empty string or null"); + } + if (!json.retries || typeof json.retries !== "object" || Array.isArray(json.retries)) { + throw new Error("Invalid Run state: retries must be an object"); + } + return fromStoredState(json); +} +function startRunWithStorage(pkg, runId, skillHash, storage) { + assertRunStorageLayout(storage); + return { + runId, + skill: pkg.definition.metadata.name, + skillVersion: pkg.definition.metadata.version, + skillHash, + orchestration: pkg.definition.orchestration.mode, + currentStep: pkg.definition.orchestration.entry ?? null, + iteration: 0, + pending: null, + pendingRef: storage.pendingRef, + trajectoryRef: storage.trajectoryRef, + contextRef: storage.contextRef, + artifactsRef: storage.artifactsRef, + checkpointRef: storage.checkpointRef, + status: "running", + retries: {} + }; +} + +// domains/comet-native/native-run-store.ts +var NATIVE_RUN_IO_LIMITS = { + runStateBytes: 256 * 1024, + trajectoryBytes: 8 * 1024 * 1024, + trajectoryEvents: 4096, + trajectoryEventBytes: 256 * 1024, + checkpointBytes: 256 * 1024, + pendingActionBytes: 256 * 1024, + contextBytes: 1024 * 1024, + artifactsBytes: 1024 * 1024 +}; +function isInside4(parent, target) { + const relative = path13.relative(parent, target); + return relative === "" || !path13.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path13.sep}`); +} +function asIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity5(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +function sameDirectoryIdentity4(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function runFile(changeDir, kind, relativePath) { + const expected = NATIVE_RUN_STORAGE[kind]; + if (relativePath !== void 0 && relativePath !== expected) { + throw new Error(`Native Run ${kind} ref must be ${expected}`); + } + const root = path13.resolve(changeDir); + const target = path13.resolve(root, ...expected.split("/")); + if (!isInside4(root, target)) throw new Error("Native Run path must stay inside its change"); + return target; +} +async function directoryIdentity2(directory) { + const stat = await fs11.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native Run parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs11.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain3(root, directory) { + const lexicalRoot = path13.resolve(root); + const lexicalDirectory = path13.resolve(directory); + if (!isInside4(lexicalRoot, lexicalDirectory)) { + throw new Error("Native Run path is outside its change"); + } + const chain = [await directoryIdentity2(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path13.relative(lexicalRoot, lexicalDirectory).split(path13.sep).filter(Boolean)) { + cursor = path13.join(cursor, segment); + let identity; + try { + identity = await directoryIdentity2(cursor); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (!isInside4(chain[0].realPath, identity.realPath)) { + throw new Error(`Native Run parent resolves outside its change: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain4(chain) { + for (const identity of chain) { + const stat = await fs11.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity4(identity, stat) || await fs11.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native Run parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded2(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readProtectedText(changeDir, file, maxBytes, label, hooks) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) return null; + await hooks?.afterParentChainCaptured?.(); + let before; + try { + before = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asIdentity(before); + const beforeRealPath = await fs11.realpath(file); + if (!isInside4(chain[0].realPath, beforeRealPath)) { + throw new Error(`${label} resolves outside its change`); + } + const flags = process.platform === "win32" ? fsConstants3.O_RDONLY : fsConstants3.O_RDONLY | fsConstants3.O_NOFOLLOW | fsConstants3.O_NONBLOCK; + let handle; + try { + handle = await fs11.open(file, flags); + } catch (error) { + throw new Error(`${label} changed while opening`, { cause: error }); + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs11.lstat(file), + fs11.realpath(file) + ]); + await verifyDirectoryChain4(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity5(beforeIdentity, opened) || !sameFileIdentity5(beforeIdentity, pathAfterOpen)) { + throw new Error(`${label} changed while opening`); + } + await hooks?.afterOpen?.(); + const bytes = await readHandleBounded2(handle, maxBytes, label); + await hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs11.lstat(file), + fs11.realpath(file) + ]); + await verifyDirectoryChain4(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity5(beforeIdentity, afterHandle) || !sameFileIdentity5(beforeIdentity, afterPath)) { + throw new Error(`${label} changed while reading`); + } + let text; + try { + text = new TextDecoder4("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`${label} is not valid UTF-8`, { cause: error }); + } + return { + text, + target: { exists: true, identity: beforeIdentity, realPath: beforeRealPath } + }; + } finally { + await handle.close(); + } +} +async function captureTarget(changeDir, file, label) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) return { exists: false }; + let stat; + try { + stat = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return { exists: false }; + throw error; + } + if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`${label} must be a regular file`); + const realPath = await fs11.realpath(file); + if (!isInside4(chain[0].realPath, realPath)) + throw new Error(`${label} resolves outside its change`); + await verifyDirectoryChain4(chain); + return { exists: true, identity: asIdentity(stat), realPath }; +} +async function verifyTarget(changeDir, file, expected, label) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) { + if (!expected.exists) return; + throw new Error(`${label} parent changed before commit`); + } + let stat; + try { + stat = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT" && !expected.exists) return; + throw new Error(`${label} changed before commit`, { cause: error }); + } + if (!expected.exists || !stat.isFile() || stat.isSymbolicLink() || !sameFileIdentity5(expected.identity, stat) || await fs11.realpath(file) !== expected.realPath) { + throw new Error(`${label} changed before commit`); + } + await verifyDirectoryChain4(chain); +} +async function writeProtectedText(options) { + const bytes = Buffer.byteLength(options.content, "utf8"); + if (bytes > options.maxBytes) { + throw new Error(`${options.label} exceeds ${options.maxBytes} bytes`); + } + const expected = options.expected ?? await captureTarget(options.changeDir, options.file, options.label); + if (expected.exists && expected.identity.size > options.maxBytes) { + throw new Error(`${options.label} exceeds ${options.maxBytes} bytes`); + } + await atomicWriteText(options.file, options.content, { + containedRoot: options.changeDir, + beforeCommit: async () => { + await options.hooks?.beforeCommit?.(); + await verifyTarget(options.changeDir, options.file, expected, options.label); + } + }); + const persisted = await readProtectedText( + options.changeDir, + options.file, + options.maxBytes, + options.label + ); + if (!persisted || persisted.text !== options.content) { + throw new Error(`${options.label} commit could not be verified`); + } +} +function parseJson(text, label) { + try { + return JSON.parse(text); + } catch (error) { + throw new Error(`${label} contains invalid JSON`, { cause: error }); + } +} +function jsonText(value) { + return `${JSON.stringify(value, null, 2)} +`; +} +function parseNativeStoredRunStateValue(value) { + return parseStoredRunStateValue(value); +} +function startNativeRun(pkg, runId, skillHash) { + return startRunWithStorage(pkg, runId, skillHash, NATIVE_RUN_STORAGE); +} +async function readNativeRunState(changeDir, hooks) { + const file = runFile(changeDir, "stateRef"); + const raw = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.runStateBytes, + "Native Run state", + hooks + ); + return raw ? parseStoredRunStateValue(parseJson(raw.text, "Native Run state")) : null; +} +async function writeNativeRunState(changeDir, state, hooks) { + const validated = parseStoredRunStateValue(state); + await writeProtectedText({ + changeDir, + file: runFile(changeDir, "stateRef"), + content: jsonText(validated), + maxBytes: NATIVE_RUN_IO_LIMITS.runStateBytes, + label: "Native Run state", + hooks + }); +} +async function readNativeTrajectory(changeDir, relativePath, hooks) { + const file = runFile(changeDir, "trajectoryRef", relativePath); + const raw = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory", + hooks + ); + if (!raw) return []; + const lines = raw.text.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter(({ line }) => line.length > 0); + if (lines.length > NATIVE_RUN_IO_LIMITS.trajectoryEvents) { + throw new Error( + `Native Run trajectory exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEvents} events` + ); + } + return lines.map(({ line, number }) => { + try { + return JSON.parse(line); + } catch (error) { + throw new Error(`Invalid Native Run trajectory event at line ${number}`, { cause: error }); + } + }); +} +async function readNativeTrajectoryText(changeDir, relativePath, hooks) { + const raw = await readProtectedText( + changeDir, + runFile(changeDir, "trajectoryRef", relativePath), + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory", + hooks + ); + return raw?.text ?? null; +} +async function replaceNativeTrajectoryText(changeDir, relativePath, content, expectedHash, hooks) { + if (!/^[a-f0-9]{64}$/u.test(expectedHash)) { + throw new Error("Native Run trajectory expected hash is invalid"); + } + const file = runFile(changeDir, "trajectoryRef", relativePath); + const existing = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory" + ); + if (!existing) throw new Error("Native Run trajectory disappeared before repair"); + const actualHash = createHash6("sha256").update(existing.text, "utf8").digest("hex"); + if (actualHash !== expectedHash) { + throw new Error("Native Run trajectory changed before repair"); + } + await writeProtectedText({ + changeDir, + file, + content, + maxBytes: NATIVE_RUN_IO_LIMITS.trajectoryBytes, + label: "Native Run trajectory", + expected: existing.target, + hooks + }); +} +async function appendNativeTrajectory(changeDir, relativePath, event, hooks) { + const file = runFile(changeDir, "trajectoryRef", relativePath); + const line = `${JSON.stringify(event)} +`; + if (Buffer.byteLength(line, "utf8") > NATIVE_RUN_IO_LIMITS.trajectoryEventBytes) { + throw new Error( + `Native Run trajectory event exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEventBytes} bytes` + ); + } + const existing = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory" + ); + const existingEvents = existing ? existing.text.split(/\r?\n/u).filter((value) => value.length > 0).length : 0; + if (existingEvents >= NATIVE_RUN_IO_LIMITS.trajectoryEvents) { + throw new Error( + `Native Run trajectory exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEvents} events` + ); + } + await writeProtectedText({ + changeDir, + file, + content: `${existing?.text ?? ""}${line}`, + maxBytes: NATIVE_RUN_IO_LIMITS.trajectoryBytes, + label: "Native Run trajectory", + expected: existing?.target ?? { exists: false }, + hooks + }); +} +async function readNativeCheckpoint(changeDir, relativePath, hooks) { + const raw = await readProtectedText( + changeDir, + runFile(changeDir, "checkpointRef", relativePath), + NATIVE_RUN_IO_LIMITS.checkpointBytes, + "Native Run checkpoint", + hooks + ); + return raw ? parseJson(raw.text, "Native Run checkpoint") : null; +} +async function writeNativeCheckpoint(changeDir, relativePath, checkpoint, hooks) { + await writeProtectedText({ + changeDir, + file: runFile(changeDir, "checkpointRef", relativePath), + content: jsonText(checkpoint), + maxBytes: NATIVE_RUN_IO_LIMITS.checkpointBytes, + label: "Native Run checkpoint", + hooks + }); +} + +// domains/comet-native/native-trajectory-recovery.ts +var CHANGE_NAME_PATTERN2 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var NativeTrajectoryRepairRequiredError = class extends Error { + constructor(inspection) { + super( + inspection.status === "repairable" ? `Native trajectory has an incomplete final line at ${inspection.file}:${inspection.line}; run doctor --repair` : `Native trajectory is invalid at ${inspection.file}:${inspection.line}: ${inspection.message}` + ); + this.inspection = inspection; + this.name = "NativeTrajectoryRepairRequiredError"; + } + inspection; + code = "native-trajectory-tail-repair-required"; +}; +function sha256Buffer(value) { + return createHash7("sha256").update(value).digest("hex"); +} +function trajectoryFile(paths, name) { + if (!CHANGE_NAME_PATTERN2.test(name)) throw new Error(`Invalid Native change name: ${name}`); + const changeDir = path14.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, changeDir)) throw new Error("Native change path escaped"); + return path14.join(changeDir, "runtime", "trajectory.jsonl"); +} +function parseCompleteLines(content) { + const lines = content.split(/\n/u); + let count = 0; + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index].endsWith("\r") ? lines[index].slice(0, -1) : lines[index]; + if (line.length === 0) continue; + count = index + 1; + try { + JSON.parse(line); + } catch (error) { + return { status: "invalid", line: index + 1, message: error.message }; + } + } + return { status: "valid", count }; +} +function looksTruncated(error, content) { + if (/Unexpected end|Unterminated string/iu.test(error.message)) return true; + const position = /position (\d+)/iu.exec(error.message)?.[1]; + return position !== void 0 && Number(position) >= Math.max(0, content.length - 1); +} +function analyzeTrajectory(file, source) { + const lastNewline = source.lastIndexOf(10); + const prefix = source.subarray(0, lastNewline + 1); + const complete = parseCompleteLines(prefix.toString("utf8")); + if (complete.status === "invalid") { + return { status: "invalid", file, line: complete.line, message: complete.message }; + } + if (lastNewline === source.length - 1) return { status: "clean", file }; + const tail = source.subarray(lastNewline + 1); + const tailText = tail.toString("utf8"); + const line = complete.count + 1; + let reason; + let target; + try { + JSON.parse(tailText.endsWith("\r") ? tailText.slice(0, -1) : tailText); + reason = "missing-newline"; + target = Buffer.concat([source, Buffer.from("\n")]); + } catch (error) { + if (!looksTruncated(error, tailText)) { + return { status: "invalid", file, line, message: error.message }; + } + reason = "incomplete-json"; + target = prefix; + } + const inspection = { + status: "repairable", + file, + reason, + line, + originalHash: sha256Buffer(source), + targetHash: sha256Buffer(target), + tailHash: sha256Buffer(tail), + discardedBytes: reason === "incomplete-json" ? tail.length : 0 + }; + return { inspection, targetContent: target.toString("utf8") }; +} +async function inspectFile(paths, name) { + const file = trajectoryFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + const changeDir = path14.join(paths.changesDir, name); + const content = await readNativeTrajectoryText(changeDir, NATIVE_RUN_STORAGE.trajectoryRef); + return content === null ? { status: "clean", file } : analyzeTrajectory(file, Buffer.from(content)); +} +async function inspectNativeTrajectoryTail(paths, name) { + const result2 = await inspectFile(paths, name); + return "inspection" in result2 ? result2.inspection : result2; +} +async function assertNativeTrajectoryHealthy(paths, name) { + const inspection = await inspectNativeTrajectoryTail(paths, name); + if (inspection.status !== "clean") throw new NativeTrajectoryRepairRequiredError(inspection); +} +async function repairNativeTrajectoryTail(paths, name, hooks) { + return withNativeMutationLock(paths, `repair trajectory tail for ${name}`, async () => { + const result2 = await inspectFile(paths, name); + if (!("inspection" in result2)) { + if (result2.status === "clean") return null; + throw new NativeTrajectoryRepairRequiredError(result2); + } + try { + await replaceNativeTrajectoryText( + path14.join(paths.changesDir, name), + NATIVE_RUN_STORAGE.trajectoryRef, + result2.targetContent, + result2.inspection.originalHash, + { beforeCommit: hooks?.beforeCommit } + ); + } catch (error) { + if (/changed (?:before|while)/iu.test(error.message)) { + throw new Error( + `Native trajectory changed while preparing tail repair for ${name}; inspect it again before retrying`, + { cause: error } + ); + } + throw error; + } + const repaired = await inspectNativeTrajectoryTail(paths, name); + if (repaired.status !== "clean") { + throw new Error(`Native trajectory tail repair did not produce a clean file for ${name}`); + } + return result2.inspection; + }); +} + +// domains/comet-native/native-workspace.ts +import { createHash as createHash8 } from "node:crypto"; +import { promises as fs12 } from "node:fs"; +import path15 from "node:path"; +var HASH_PATTERN2 = /^[a-f0-9]{64}$/u; +var MAX_WORKSPACE_IDENTITY_BYTES = 16 * 1024; +var NATIVE_WORKSPACE_ADVISORY_CODES = /* @__PURE__ */ new Set([ + "workspace-root-changed", + "workspace-inspection-unavailable" +]); +function isNativeWorkspaceAdvisoryCode(code) { + return NATIVE_WORKSPACE_ADVISORY_CODES.has(code); +} +function portableRelative2(parent, target) { + const relative = path15.relative(parent, target); + if (path15.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path15.sep}`)) { + return null; + } + return relative.replaceAll("\\", "/") || "."; +} +function hasControlCharacter(value) { + return Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }); +} +function normalizedPortableRef(value, label) { + if (value.length === 0 || hasControlCharacter(value) || value.includes("\\") || path15.posix.isAbsolute(value) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..")) { + throw new Error(`${label} must be a portable project-relative path`); + } + const normalized = path15.posix.normalize(value); + if (normalized !== value || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return normalized; +} +function identityHash(tag, value) { + return createHash8("sha256").update(`${tag} +${value}`).digest("hex"); +} +async function physicalDirectoryIdentity(tag, value) { + const realPath = await fs12.realpath(value); + const stat = await fs12.lstat(realPath); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error("Native workspace identity requires a real directory"); + } + const normalizedPath = process.platform === "win32" ? path15.normalize(realPath).toLowerCase() : realPath; + return identityHash(tag, `${normalizedPath} +${stat.dev} +${stat.ino} +${stat.birthtimeMs}`); +} +async function directoryPathIdentity(tag, value) { + const realPath = await fs12.realpath(value); + const stat = await fs12.lstat(realPath); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error("Native workspace identity requires a real directory"); + } + const normalizedPath = process.platform === "win32" ? path15.normalize(realPath).toLowerCase() : realPath; + return identityHash(tag, normalizedPath); +} +function isoTimestamp(value) { + if (typeof value !== "string") throw new Error("Native workspace capturedAt is invalid"); + const parsed = new Date(value); + if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) { + throw new Error("Native workspace capturedAt is invalid"); + } + return value; +} +function assertIdentity(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native workspace identity must be an object"); + } + const root = value; + const allowed = /* @__PURE__ */ new Set([ + "schema", + "capturedAt", + "capturedRevision", + "nativeRootRef", + "projectRootId", + "nativeRootId", + "projectRootPathId", + "nativeRootPathId", + "sessionHash" + ]); + const unknown = Object.keys(root).filter((key) => !allowed.has(key)); + if (unknown.length > 0) { + throw new Error(`Native workspace identity has unknown field(s): ${unknown.join(", ")}`); + } + if (root.schema !== "comet.native.workspace.v2") { + throw new Error("Unsupported Native workspace identity"); + } + if (!Number.isSafeInteger(root.capturedRevision) || root.capturedRevision < 1 || typeof root.nativeRootRef !== "string" || !HASH_PATTERN2.test(String(root.projectRootId)) || !HASH_PATTERN2.test(String(root.nativeRootId))) { + throw new Error("Native workspace identity is invalid"); + } + isoTimestamp(root.capturedAt); + normalizedPortableRef(root.nativeRootRef, "Native workspace root ref"); + const hasProjectPathId = root.projectRootPathId !== void 0; + const hasNativePathId = root.nativeRootPathId !== void 0; + if (hasProjectPathId !== hasNativePathId) { + throw new Error("Native workspace path identities must be provided together"); + } + if (hasProjectPathId && !HASH_PATTERN2.test(String(root.projectRootPathId)) || hasNativePathId && !HASH_PATTERN2.test(String(root.nativeRootPathId))) { + throw new Error("Native workspace path identity is invalid"); + } + if (root.sessionHash !== void 0 && !HASH_PATTERN2.test(String(root.sessionHash))) { + throw new Error("Native workspace session hash is invalid"); + } +} +function nativeWorkspaceFile(paths, name) { + return path15.join(paths.changesDir, name, "runtime", "workspace.json"); +} +function nativeWorkspaceRef(paths, name) { + const relative = portableRelative2(paths.nativeRoot, nativeWorkspaceFile(paths, name)); + if (!relative || relative === ".") throw new Error("Native workspace file escaped its root"); + return normalizedPortableRef(relative, "Native workspace file ref"); +} +async function readNativeWorkspaceValue(paths, name) { + try { + const artifact = await readNativeBoundedTextFile({ + root: paths.nativeRoot, + ref: nativeWorkspaceRef(paths, name), + maxBytes: MAX_WORKSPACE_IDENTITY_BYTES + }); + return JSON.parse(artifact.text); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function nativeWorkspaceIdentityNeedsMigration(paths, name) { + const value = await readNativeWorkspaceValue(paths, name); + if (value === null) return false; + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native workspace identity must be an object"); + } + if (value.schema === "comet.native.workspace.v1") return true; + assertIdentity(value); + return value.projectRootPathId === void 0 || value.nativeRootPathId === void 0; +} +async function inspectNativeWorkspaceIdentity(options) { + if (!Number.isSafeInteger(options.revision) || options.revision < 1) { + throw new Error("Native workspace revision must be a positive integer"); + } + const nativeRootRef3 = portableRelative2(options.paths.projectRoot, options.paths.nativeRoot); + if (!nativeRootRef3) throw new Error("Native root is outside the project root"); + const [projectRootId, nativeRootId, projectRootPathId, nativeRootPathId] = await Promise.all([ + physicalDirectoryIdentity("comet.native.workspace-project-root.v2", options.paths.projectRoot), + physicalDirectoryIdentity("comet.native.workspace-native-root.v2", options.paths.nativeRoot), + directoryPathIdentity("comet.native.workspace-project-root-path.v2", options.paths.projectRoot), + directoryPathIdentity("comet.native.workspace-native-root-path.v2", options.paths.nativeRoot) + ]); + const capturedAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString(); + const identity = { + schema: "comet.native.workspace.v2", + capturedAt, + capturedRevision: options.revision, + nativeRootRef: nativeRootRef3, + projectRootId, + nativeRootId, + projectRootPathId, + nativeRootPathId, + ...options.sessionId ? { + sessionHash: identityHash( + "comet.native.workspace-session.v2", + `${projectRootId} +${nativeRootId} +${options.sessionId}` + ) + } : {} + }; + assertIdentity(identity); + return identity; +} +async function writeNativeWorkspaceIdentity(options) { + const identity = await inspectNativeWorkspaceIdentity(options); + const file = nativeWorkspaceFile(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, file); + await atomicWriteJson(file, identity, { containedRoot: options.paths.nativeRoot }); + return identity; +} +async function readNativeWorkspaceIdentity(paths, name) { + const value = await readNativeWorkspaceValue(paths, name); + if (value === null) return null; + if (value && typeof value === "object" && !Array.isArray(value) && value.schema === "comet.native.workspace.v1") { + return null; + } + assertIdentity(value); + return value; +} +async function migrateLegacyNativeWorkspaceIdentity(options) { + if (!await nativeWorkspaceIdentityNeedsMigration(options.paths, options.name)) { + return null; + } + return writeNativeWorkspaceIdentity(options); +} +async function inspectNativeWorkspaceAdvisory(options) { + assertIdentity(options.identity); + const current = await inspectNativeWorkspaceIdentity({ + paths: options.paths, + name: "workspace-advisory", + revision: options.identity.capturedRevision + }); + const driftComponents = []; + const codes = []; + if (current.nativeRootRef !== options.identity.nativeRootRef) { + driftComponents.push("native-root-ref"); + } + if (options.identity.projectRootPathId && options.identity.nativeRootPathId) { + if (current.projectRootPathId !== options.identity.projectRootPathId) { + driftComponents.push("project-root-path"); + } + if (current.nativeRootPathId !== options.identity.nativeRootPathId) { + driftComponents.push("native-root-path"); + } + } else { + if (current.projectRootId !== options.identity.projectRootId) { + driftComponents.push("project-root-legacy-identity"); + } + if (current.nativeRootId !== options.identity.nativeRootId) { + driftComponents.push("native-root-legacy-identity"); + } + } + const onlyUnstableWindowsLegacyHashes = process.platform === "win32" && driftComponents.length > 0 && driftComponents.every( + (component) => component === "project-root-legacy-identity" || component === "native-root-legacy-identity" + ); + if (onlyUnstableWindowsLegacyHashes) { + codes.push("workspace-inspection-unavailable"); + } else if (driftComponents.length > 0) { + codes.push("workspace-root-changed"); + } + return { + state: codes.length === 0 ? "aligned" : codes.includes("workspace-root-changed") ? "drifted" : "unknown", + findingCodes: codes, + driftComponents + }; +} + +// domains/comet-native/native-types.ts +var NATIVE_RUNTIME_PROTOCOL_VERSION = 3; +var NATIVE_CHANGE_SCHEMA = "comet.native.v3"; +var NATIVE_V2_CHANGE_SCHEMA = "comet.native.v2"; +var NATIVE_LEGACY_CHANGE_SCHEMA = "comet.native.v1"; +var NATIVE_TRANSITION_SCHEMA = "comet.native.transition.v3"; +var NATIVE_V2_TRANSITION_SCHEMA = "comet.native.transition.v2"; +var NATIVE_LEGACY_TRANSITION_SCHEMA = "comet.native.transition.v1"; + +// domains/comet-native/native-change.ts +var CHANGE_KEYS = [ + "schema", + "name", + "language", + "phase", + "brief", + "approval", + "spec_changes", + "verification_result", + "verification_report", + "archived", + "created_at", + "run_id" +]; +var LEGACY_CHANGE_KEYS = new Set(CHANGE_KEYS); +var V2_CHANGE_KEYS = /* @__PURE__ */ new Set([...CHANGE_KEYS, "minimum_runtime_version", "revision"]); +var CURRENT_CHANGE_KEYS = /* @__PURE__ */ new Set([ + ...V2_CHANGE_KEYS, + "approved_contract_hash", + "implementation_scope", + "verification_evidence", + "partial_allowance" +]); +var SPEC_CHANGE_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "source", "base_hash"]); +var PHASES = /* @__PURE__ */ new Set(["shape", "build", "verify", "archive"]); +var APPROVALS = /* @__PURE__ */ new Set(["implicit", "confirmed"]); +var VERIFY_RESULTS = /* @__PURE__ */ new Set(["pending", "pass", "fail"]); +var NATIVE_CHANGE_STATE_FILE = "comet-state.yaml"; +var HASH_PATTERN3 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CONTENT_ADDRESSED_REF_PATTERN = /^runtime\/evidence\/(scopes|allowances|verifications)\/([a-f0-9]{64})\.json$/u; +var NativeSchemaMigrationRequiredError = class extends Error { + constructor(change, schema) { + super( + `Native change ${change} uses ${schema}; run comet native doctor ${change} --repair before mutating it` + ); + this.change = change; + this.schema = schema; + this.name = "NativeSchemaMigrationRequiredError"; + } + change; + schema; + code = "native-schema-migration-required"; +}; +var NativeRuntimeCompatibilityError = class extends Error { + constructor(schema, minimumRuntimeVersion) { + super( + schema !== NATIVE_CHANGE_SCHEMA || minimumRuntimeVersion === null ? `Unsupported Native change schema ${schema} for runtime protocol ${NATIVE_RUNTIME_PROTOCOL_VERSION}` : `Native change ${schema} requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + this.schema = schema; + this.minimumRuntimeVersion = minimumRuntimeVersion; + this.name = "NativeRuntimeCompatibilityError"; + } + schema; + minimumRuntimeVersion; + code = "native-runtime-incompatible"; +}; +var NativeChangeRevisionConflictError = class extends Error { + constructor(change, expectedRevision, actualRevision) { + super( + `Native change ${change} revision conflict: expected ${expectedRevision}, actual ${actualRevision}` + ); + this.change = change; + this.expectedRevision = expectedRevision; + this.actualRevision = actualRevision; + this.name = "NativeChangeRevisionConflictError"; + } + change; + expectedRevision; + actualRevision; + code = "native-change-revision-conflict"; +}; +var NativeBaselineIncompleteError = class extends Error { + constructor(change, omittedCount, omittedByReason, samplePaths, sampleTruncated, effectiveLimits = null, policyHash = null) { + super( + `Native change ${change} baseline is incomplete (${omittedCount} omitted entr${omittedCount === 1 ? "y" : "ies"}). Adjust native.snapshot scope or resource budgets in .comet/config.yaml, then retry.` + ); + this.change = change; + this.omittedCount = omittedCount; + this.omittedByReason = omittedByReason; + this.samplePaths = samplePaths; + this.sampleTruncated = sampleTruncated; + this.effectiveLimits = effectiveLimits; + this.policyHash = policyHash; + this.name = "NativeBaselineIncompleteError"; + } + change; + omittedCount; + omittedByReason; + samplePaths; + sampleTruncated; + effectiveLimits; + policyHash; + code = "native-baseline-incomplete"; +}; +var NATIVE_BRIEF_TEMPLATE = [ + "# Outcome", + "", + "# Scope", + "", + "# Non-goals", + "", + "# Acceptance examples", + "", + "# Constraints and invariants", + "", + "# Decisions", + "", + "# Open questions", + "", + "# Verification expectations", + "" +].join("\n"); +function record4(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown4(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function assertNativeName(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native change name: ${value}`); +} +function assertCapabilityId(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native capability id: ${value}`); +} +function assertRelativeRef(value, label) { + if (value.length === 0 || path16.isAbsolute(value) || /^(?:[A-Za-z]:|~|[\\/])/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native change`); + } +} +function parseSpecChange(value, index) { + const item = record4(value, `spec_changes[${index}]`); + rejectUnknown4(item, SPEC_CHANGE_KEYS, `spec_changes[${index}]`); + if (typeof item.capability !== "string") throw new Error("spec change capability is required"); + assertCapabilityId(item.capability); + if (item.operation !== "create" && item.operation !== "replace" && item.operation !== "remove") { + throw new Error(`Invalid spec operation for ${item.capability}`); + } + const source = item.source; + const baseHash = item.base_hash; + if (source !== void 0 && typeof source !== "string") { + throw new Error(`Spec source for ${item.capability} must be a string`); + } + if (typeof source === "string") assertRelativeRef(source, `Spec source for ${item.capability}`); + if (item.operation === "create") { + if (!source) throw new Error(`Create spec ${item.capability} requires source`); + if (baseHash !== null) + throw new Error(`Create spec ${item.capability} requires null base_hash`); + } else if (item.operation === "replace") { + if (!source) throw new Error(`Replace spec ${item.capability} requires source`); + if (typeof baseHash !== "string" || !HASH_PATTERN3.test(baseHash)) { + throw new Error(`Replace spec ${item.capability} requires a SHA-256 base_hash`); + } + } else { + if (source !== void 0) throw new Error(`Remove spec ${item.capability} forbids source`); + if (typeof baseHash !== "string" || !HASH_PATTERN3.test(baseHash)) { + throw new Error(`Remove spec ${item.capability} requires a SHA-256 base_hash`); + } + } + return { + capability: item.capability, + operation: item.operation, + ...typeof source === "string" ? { source } : {}, + base_hash: baseHash + }; +} +function validDate(value) { + if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) return false; + return (/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`)).toISOString().slice(0, 10) === value; +} +function parseChangeFields(root, knownKeys) { + rejectUnknown4(root, knownKeys, NATIVE_CHANGE_STATE_FILE); + if (typeof root.name !== "string") throw new Error("Native change name is required"); + assertNativeName(root.name); + if (root.language !== "en" && root.language !== "zh-CN") { + throw new Error("Native change language must be en or zh-CN"); + } + if (typeof root.phase !== "string" || !PHASES.has(root.phase)) { + throw new Error("Native change phase is invalid"); + } + if (root.brief !== "brief.md") throw new Error("Native change brief must be brief.md"); + if (root.approval !== null && !APPROVALS.has(root.approval)) { + throw new Error("Native change approval is invalid"); + } + if (!Array.isArray(root.spec_changes)) throw new Error("Native spec_changes must be an array"); + const specChanges = root.spec_changes.map(parseSpecChange); + const duplicates = specChanges.map((change) => change.capability).filter((capability, index, all) => all.indexOf(capability) !== index); + if (duplicates.length > 0) { + throw new Error( + `Duplicate Native capability operation: ${[...new Set(duplicates)].join(", ")}` + ); + } + if (typeof root.verification_result !== "string" || !VERIFY_RESULTS.has(root.verification_result)) { + throw new Error("Native verification_result is invalid"); + } + if (root.verification_report !== null && typeof root.verification_report !== "string") { + throw new Error("Native verification_report must be a string or null"); + } + if (typeof root.verification_report === "string") { + assertRelativeRef(root.verification_report, "Native verification_report"); + } + if (typeof root.archived !== "boolean") throw new Error("Native archived must be boolean"); + if (typeof root.created_at !== "string" || !validDate(root.created_at)) { + throw new Error("Native created_at must be a valid YYYY-MM-DD date"); + } + if (root.run_id !== null && (typeof root.run_id !== "string" || root.run_id.length === 0)) { + throw new Error("Native run_id must be a non-empty string or null"); + } + return { + name: root.name, + language: root.language, + phase: root.phase, + brief: "brief.md", + approval: root.approval, + spec_changes: specChanges, + verification_result: root.verification_result, + verification_report: root.verification_report, + archived: root.archived, + created_at: root.created_at, + run_id: root.run_id + }; +} +function parseLegacyNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_LEGACY_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_LEGACY_CHANGE_SCHEMA}`); + } + return { + schema: NATIVE_LEGACY_CHANGE_SCHEMA, + ...parseChangeFields(root, LEGACY_CHANGE_KEYS) + }; +} +function positiveInteger2(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function contentAddressedRef(value, label, kind) { + if (value === null) return null; + const match = typeof value === "string" ? CONTENT_ADDRESSED_REF_PATTERN.exec(value) : null; + if (!match || match[1] !== kind) { + throw new Error( + `${label} must be null or runtime/evidence/${kind}/.json relative to the Native change` + ); + } + return value; +} +function approvedContractHash(value) { + if (value === void 0 || value === null) return null; + if (typeof value !== "string" || !HASH_PATTERN3.test(value)) { + throw new Error("Native approved_contract_hash must be null or a SHA-256 hash"); + } + return value; +} +function parseV2NativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_V2_CHANGE_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native v2 minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error(`Native ${NATIVE_V2_CHANGE_SCHEMA} minimum_runtime_version must be 2`); + } + return { + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision: positiveInteger2(root.revision, "Native v2 revision"), + ...parseChangeFields(root, V2_CHANGE_KEYS) + }; +} +function parseNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA || root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const previous = root.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? parseLegacyNativeChangeValue(root) : parseV2NativeChangeValue(root); + throw new NativeSchemaMigrationRequiredError(previous.name, previous.schema); + } + throw new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + typeof root.minimum_runtime_version === "number" ? root.minimum_runtime_version : null + ); + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native ${root.schema} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger2(root.revision, "Native revision"); + const fields = parseChangeFields(root, CURRENT_CHANGE_KEYS); + const approvalHash = approvedContractHash(root.approved_contract_hash); + if (fields.approval === null && approvalHash !== null) { + throw new Error("Native approved_contract_hash requires an approval"); + } + return { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + ...fields, + approved_contract_hash: approvalHash, + implementation_scope: contentAddressedRef( + root.implementation_scope, + "Native implementation_scope", + "scopes" + ), + verification_evidence: contentAddressedRef( + root.verification_evidence, + "Native verification_evidence", + "verifications" + ), + partial_allowance: contentAddressedRef( + root.partial_allowance, + "Native partial_allowance", + "allowances" + ) + }; +} +function inspectNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + const state2 = parseLegacyNativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: 1, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const state2 = parseV2NativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: state2.minimum_runtime_version, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + const minimumRuntimeVersion2 = typeof root.minimum_runtime_version === "number" && Number.isSafeInteger(root.minimum_runtime_version) ? root.minimum_runtime_version : null; + return { + status: "runtime-incompatible", + schema: typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion: minimumRuntimeVersion2, + state: null, + message: new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion2 + ).message + }; + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + return { + status: "runtime-incompatible", + schema: root.schema, + minimumRuntimeVersion, + state: null, + message: new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion).message + }; + } + const state = parseNativeChangeValue(root); + return { + status: "current", + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + state + }; +} +function nativeChangeDocument(state) { + const parsed = parseNativeChangeValue(state); + return { + schema: parsed.schema, + minimum_runtime_version: parsed.minimum_runtime_version, + revision: parsed.revision, + name: parsed.name, + language: parsed.language, + phase: parsed.phase, + brief: parsed.brief, + approval: parsed.approval, + approved_contract_hash: parsed.approved_contract_hash ?? null, + spec_changes: parsed.spec_changes.map((change) => ({ + capability: change.capability, + operation: change.operation, + ...change.source ? { source: change.source } : {}, + base_hash: change.base_hash + })), + verification_result: parsed.verification_result, + verification_report: parsed.verification_report, + implementation_scope: parsed.implementation_scope, + verification_evidence: parsed.verification_evidence, + partial_allowance: parsed.partial_allowance, + archived: parsed.archived, + created_at: parsed.created_at, + run_id: parsed.run_id + }; +} +function nativeV2ChangeDocument(state) { + const parsed = parseV2NativeChangeValue(state); + return { + schema: parsed.schema, + minimum_runtime_version: parsed.minimum_runtime_version, + revision: parsed.revision, + name: parsed.name, + language: parsed.language, + phase: parsed.phase, + brief: parsed.brief, + approval: parsed.approval, + spec_changes: parsed.spec_changes.map((change) => ({ + capability: change.capability, + operation: change.operation, + ...change.source ? { source: change.source } : {}, + base_hash: change.base_hash + })), + verification_result: parsed.verification_result, + verification_report: parsed.verification_report, + archived: parsed.archived, + created_at: parsed.created_at, + run_id: parsed.run_id + }; +} +function nativeChangeDir(paths, name) { + assertNativeName(name); + const target = path16.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, target)) throw new Error("Native change path escaped"); + return target; +} +async function hasPendingNativeSchemaMigration(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function hasPendingNativeCheckpointRecovery(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), "runtime", "checkpoint-journal.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function createNativeChange(options) { + return withNativeMutationLock( + options.paths, + `create change ${options.name}`, + () => createNativeChangeLocked(options) + ); +} +async function createNativeChangeLocked(options) { + assertNativeName(options.name); + const changeDir = nativeChangeDir(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, changeDir); + let createdChangeDir = false; + try { + try { + await fs13.mkdir(changeDir, { recursive: false }); + createdChangeDir = true; + } catch (error) { + if (error.code === "ENOENT") { + await fs13.mkdir(options.paths.changesDir, { recursive: true }); + try { + await fs13.mkdir(changeDir, { recursive: false }); + createdChangeDir = true; + } catch (retryError) { + if (retryError.code === "EEXIST") { + throw new Error(`Native change already exists: ${options.name}`, { + cause: retryError + }); + } + throw retryError; + } + } else if (error.code === "EEXIST") { + throw new Error(`Native change already exists: ${options.name}`, { cause: error }); + } else { + throw error; + } + } + const state = { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + name: options.name, + language: options.language, + phase: "shape", + brief: "brief.md", + approval: null, + approved_contract_hash: null, + spec_changes: [], + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: false, + created_at: (options.now ?? /* @__PURE__ */ new Date()).toISOString().slice(0, 10), + run_id: null + }; + await Promise.all([ + fs13.mkdir(path16.join(changeDir, "specs"), { recursive: true }), + fs13.mkdir(path16.join(changeDir, "runtime", "checkpoints"), { recursive: true }), + atomicWriteText(path16.join(changeDir, "brief.md"), NATIVE_BRIEF_TEMPLATE) + ]); + const projectConfig = await readProjectConfig(options.paths.projectRoot); + const snapshot2 = projectConfig?.native.snapshot ?? DEFAULT_NATIVE_SNAPSHOT_CONFIG; + const baseline = await createNativeContentSnapshot(options.paths, { + now: options.now, + origin: "change-created", + policy: snapshot2, + limits: { + maxFiles: snapshot2.max_files, + maxFileBytes: snapshot2.max_total_bytes, + maxTotalBytes: snapshot2.max_total_bytes, + maxDurationMs: snapshot2.max_duration_ms + }, + deadlineMs: snapshot2.max_duration_ms + }); + if (!baseline.complete) { + const health = inspectNativeContentSnapshotHealth(baseline); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + throw new NativeBaselineIncompleteError( + state.name, + baseline.omittedCount, + omittedByReason, + health.samplePaths, + health.sampleTruncated, + baseline.limits, + baseline.policy?.hash ?? null + ); + } + await writeNativeBaselineManifest(options.paths, state.name, baseline); + await createNativeChangeFile(options.paths, state); + await writeNativeWorkspaceIdentity({ + paths: options.paths, + name: state.name, + revision: state.revision, + now: options.now + }); + return state; + } catch (error) { + if (createdChangeDir) await fs13.rm(changeDir, { recursive: true, force: true }); + throw error; + } +} +var NATIVE_CHANGE_DOCUMENT_MAX_BYTES = 256 * 1024; +async function readChangeDocumentFile(file, root = path16.dirname(file)) { + const ref = path16.relative(root, file).split(path16.sep).join("/"); + const source = await readNativeBoundedTextFile({ + root, + ref, + maxBytes: NATIVE_CHANGE_DOCUMENT_MAX_BYTES + }); + const document = (0, import_yaml3.parseDocument)(source.text, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid Native change file ${file}: ${document.errors[0].message}`); + } + return document.toJS(); +} +async function inspectNativeChange(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + const inspection = inspectNativeChangeValue(await readChangeDocumentFile(file, paths.nativeRoot)); + if (inspection.state && inspection.state.name !== name) { + throw new Error(`Native change directory/name mismatch: ${name}`); + } + if (await hasPendingNativeSchemaMigration(paths, name)) { + return { + status: "migration-required", + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + state: inspection.state, + message: `Native schema migration is incomplete for ${name}; run doctor --repair` + }; + } + return inspection; +} +async function readNativeChange(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + throw new NativeSchemaMigrationRequiredError(name, inspection.schema); + } + if (inspection.status === "runtime-incompatible" || !inspection.state) { + throw new NativeRuntimeCompatibilityError(inspection.schema, inspection.minimumRuntimeVersion); + } + return inspection.state; +} +async function createNativeChangeFile(paths, state) { + const file = path16.join(nativeChangeDir(paths, state.name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.access(file); + throw new Error(`Native change state already exists: ${state.name}`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + if (state.revision !== 1) throw new Error("New Native change must start at revision 1"); + await atomicWriteText(file, (0, import_yaml3.stringify)(nativeChangeDocument(state))); +} +async function compareAndSwapNativeChangeFile(file, state, expectedRevision) { + const next = { + ...state, + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: expectedRevision + 1 + }; + const result2 = await compareAndSwapNativeRevision({ + expectedRevision, + next, + read: async () => { + const current = parseNativeChangeValue(await readChangeDocumentFile(file)); + if (current.name !== state.name) { + throw new Error(`Native change file/name mismatch: ${state.name}`); + } + return current; + }, + write: (value) => atomicWriteText(file, (0, import_yaml3.stringify)(nativeChangeDocument(value))), + equals: (left, right) => JSON.stringify(nativeChangeDocument(left)) === JSON.stringify(nativeChangeDocument(right)), + conflict: (actualRevision) => new NativeChangeRevisionConflictError(state.name, expectedRevision, actualRevision) + }); + Object.assign(state, result2); + return result2; +} +async function compareAndSwapNativeChangeLocked(paths, state, expectedRevision, options) { + await assertNoPendingNativeRootMove(paths.projectRoot); + if (await hasPendingNativeSchemaMigration(paths, state.name)) { + throw new NativeSchemaMigrationRequiredError(state.name, state.schema); + } + if (!options?.allowPendingCheckpointRecovery && await hasPendingNativeCheckpointRecovery(paths, state.name)) { + throw new Error( + `Native progress checkpoint recovery is required for ${state.name} before another state write` + ); + } + await assertNativeTrajectoryHealthy(paths, state.name); + const file = path16.join(nativeChangeDir(paths, state.name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + return compareAndSwapNativeChangeFile(file, state, expectedRevision); +} +async function writeNativeChangeFile(file, state) { + return compareAndSwapNativeChangeFile(file, state, state.revision); +} +async function readNativeChangeFile(file) { + return parseNativeChangeValue(await readChangeDocumentFile(file)); +} + +// domains/comet-native/native-artifacts.ts +var BRIEF_REQUIRED = ["Outcome", "Scope", "Non-goals", "Acceptance examples"]; +var BRIEF_ALL = [ + ...BRIEF_REQUIRED, + "Constraints and invariants", + "Decisions", + "Open questions", + "Verification expectations" +]; +var VERIFICATION_ALL = [ + "Acceptance evidence", + "Commands and results", + "Skipped checks", + "Spec consistency", + "Known limitations and risks", + "Conclusion" +]; +var NATIVE_ARTIFACT_VALIDATION_LIMITS = { + maxFileBytes: DEFAULT_NATIVE_ARTIFACT_MAX_BYTES +}; +function markdownSections(source) { + const sections = /* @__PURE__ */ new Map(); + let heading = null; + let body = []; + const flush = () => { + if (heading !== null) sections.set(heading, body.join("\n").trim()); + }; + for (const line of source.split(/\r?\n/u)) { + const match = /^# ([^#].*)$/u.exec(line); + if (match) { + flush(); + heading = match[1].trim(); + body = []; + } else if (heading !== null) { + body.push(line); + } + } + flush(); + return sections; +} +async function readContainedFile(root, relativeRef) { + const target = path17.resolve(root, ...relativeRef.split(/[\\/]/u)); + if (!isInsidePath(root, target)) + throw new Error(`Artifact escapes Native change: ${relativeRef}`); + const realRoot = await fs14.realpath(root); + const realTarget = await fs14.realpath(target); + if (!isInsidePath(realRoot, realTarget)) { + throw new Error(`Artifact symlink escapes Native change: ${relativeRef}`); + } + if (!(await fs14.stat(realTarget)).isFile()) + throw new Error(`Artifact is not a file: ${relativeRef}`); + return realTarget; +} +function result(findings) { + return { valid: findings.length === 0, findings }; +} +async function validateNativeBrief(changeDir, briefRef) { + const findings = []; + let source; + try { + source = (await readNativeBoundedTextFile({ + root: changeDir, + ref: briefRef, + maxBytes: NATIVE_ARTIFACT_VALIDATION_LIMITS.maxFileBytes + })).text; + } catch (error) { + return result([{ code: "brief-missing", message: error.message, path: briefRef }]); + } + const sections = markdownSections(source); + for (const heading of BRIEF_ALL) { + if (!sections.has(heading)) { + findings.push({ + code: "brief-section-missing", + message: `Missing brief section: ${heading}`, + path: briefRef + }); + } + } + for (const heading of BRIEF_REQUIRED) { + if ((sections.get(heading) ?? "").length === 0) { + findings.push({ + code: "brief-section-empty", + message: `Brief section is empty: ${heading}`, + path: briefRef + }); + } + } + const openQuestions = sections.get("Open questions") ?? ""; + if (/^\s*-\s*\[blocking\]/imu.test(openQuestions)) { + findings.push({ + code: "brief-blocking-question", + message: "Brief has a blocking open question", + path: briefRef + }); + } + return result(findings); +} +async function validateNativeVerification(changeDir, reportRef) { + const findings = []; + let source; + try { + source = (await readNativeBoundedTextFile({ + root: changeDir, + ref: reportRef, + maxBytes: NATIVE_ARTIFACT_VALIDATION_LIMITS.maxFileBytes + })).text; + } catch (error) { + return result([ + { code: "verification-missing", message: error.message, path: reportRef } + ]); + } + const sections = markdownSections(source); + for (const heading of VERIFICATION_ALL) { + if (!sections.has(heading)) { + findings.push({ + code: "verification-section-missing", + message: `Missing verification section: ${heading}`, + path: reportRef + }); + } else if ((sections.get(heading) ?? "").length === 0) { + findings.push({ + code: "verification-section-empty", + message: `Verification section is empty: ${heading}`, + path: reportRef + }); + } + } + return result(findings); +} +function canonicalSpecPath(paths, capability) { + return path17.join(paths.specsDir, capability, "spec.md"); +} +async function validateNativeSpecChanges(paths, state) { + const findings = []; + const changeDir = nativeChangeDir(paths, state.name); + for (const change of state.spec_changes) { + const canonical = canonicalSpecPath(paths, change.capability); + let canonicalHash2 = null; + try { + await resolveContainedNativePath(paths.nativeRoot, canonical); + canonicalHash2 = await sha256File(canonical); + } catch (error) { + if (error.code !== "ENOENT") { + findings.push({ + code: "spec-canonical-unsafe", + message: error.message, + path: canonical + }); + continue; + } + } + if (change.operation === "create" && canonicalHash2 !== null) { + findings.push({ + code: "spec-create-exists", + message: `Canonical spec already exists: ${change.capability}`, + path: canonical + }); + } + if (change.operation !== "create" && canonicalHash2 === null) { + findings.push({ + code: "spec-base-missing", + message: `Canonical spec is missing: ${change.capability}`, + path: canonical + }); + } + if (change.operation !== "create" && canonicalHash2 !== null && canonicalHash2 !== change.base_hash) { + findings.push({ + code: "spec-base-conflict", + message: `Canonical spec changed for ${change.capability}: expected ${change.base_hash}, actual ${canonicalHash2}`, + path: canonical + }); + } + if (change.source) { + try { + await readContainedFile(changeDir, change.source); + } catch (error) { + findings.push({ + code: "spec-source-invalid", + message: error.message, + path: change.source + }); + } + } + } + return result(findings); +} +async function resolveNativeArtifactFile(changeDir, relativeRef) { + return readContainedFile(changeDir, relativeRef); +} + +// domains/comet-native/native-archive-inspection.ts +import { promises as fs18 } from "node:fs"; +import path29 from "node:path"; + +// domains/comet-native/native-archive-preflight.ts +import path18 from "node:path"; + +// domains/comet-native/native-canonical-hash.ts +import { createHash as createHash9 } from "crypto"; +function invalidCanonicalJson(detail) { + throw new TypeError(`Value is not valid canonical JSON: ${detail}`); +} +function canonicalArray(value, ancestors) { + if (ancestors.has(value)) invalidCanonicalJson("cyclic structures are not supported"); + ancestors.add(value); + try { + const enumerableKeys = Object.keys(value); + for (let index = 0; index < value.length; index += 1) { + if (!Object.prototype.hasOwnProperty.call(value, index)) { + invalidCanonicalJson("sparse arrays are not supported"); + } + } + if (enumerableKeys.length !== value.length || enumerableKeys.some((key, index) => key !== String(index))) { + invalidCanonicalJson("arrays must not have named enumerable properties"); + } + if (Object.getOwnPropertySymbols(value).length > 0) { + invalidCanonicalJson("symbol properties are not supported"); + } + return `[${value.map((entry2) => canonicalValue(entry2, ancestors)).join(",")}]`; + } finally { + ancestors.delete(value); + } +} +function canonicalObject(value, ancestors) { + const prototype = Object.getPrototypeOf(value); + if (prototype !== Object.prototype && prototype !== null) { + invalidCanonicalJson("only plain objects are supported"); + } + if (ancestors.has(value)) invalidCanonicalJson("cyclic structures are not supported"); + if (Object.getOwnPropertySymbols(value).length > 0) { + invalidCanonicalJson("symbol properties are not supported"); + } + ancestors.add(value); + try { + const descriptors = Object.getOwnPropertyDescriptors(value); + const keys = Object.keys(value).sort(); + const fields = keys.map((key) => { + const descriptor = descriptors[key]; + if (!descriptor || !("value" in descriptor)) { + invalidCanonicalJson("accessor properties are not supported"); + } + return `${JSON.stringify(key)}:${canonicalValue(descriptor.value, ancestors)}`; + }); + return `{${fields.join(",")}}`; + } finally { + ancestors.delete(value); + } +} +function canonicalValue(value, ancestors) { + if (value === null) return "null"; + switch (typeof value) { + case "boolean": + return value ? "true" : "false"; + case "string": + return JSON.stringify(value); + case "number": + if (!Number.isFinite(value)) invalidCanonicalJson("numbers must be finite"); + return JSON.stringify(Object.is(value, -0) ? 0 : value); + case "object": + return Array.isArray(value) ? canonicalArray(value, ancestors) : canonicalObject(value, ancestors); + case "bigint": + case "function": + case "symbol": + case "undefined": + return invalidCanonicalJson(`${typeof value} values are not supported`); + } + return invalidCanonicalJson("unsupported value type"); +} +function canonicalJson(value) { + return canonicalValue(value, /* @__PURE__ */ new Set()); +} +function canonicalHash(tag, value) { + if (tag.length === 0) throw new TypeError("Canonical hash tag must be non-empty"); + if (/[\r\n]/u.test(tag)) { + throw new TypeError("Canonical hash tag must not contain a line break"); + } + return createHash9("sha256").update(`${tag} +${canonicalJson(value)}`).digest("hex"); +} + +// domains/comet-native/native-archive-preflight.ts +var NATIVE_ARCHIVE_PREFLIGHT_SCHEMA = "comet.native.archive-preflight.v1"; +var PREFLIGHT_HASH_TAG = "comet.native.archive-preflight.v1"; +var OPERATION_HASH_TAG = "comet.native.archive-operation.v1"; +var HASH_PATTERN4 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN2 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var FINDING_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +function compareText(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function hash(value, label) { + if (typeof value !== "string" || !HASH_PATTERN4.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function optionalHash(value, label) { + return value === null ? null : hash(value, label); +} +function normalizedRef(value, label) { + const normalized = path18.posix.normalize(value); + if (typeof value !== "string" || value.length === 0 || value !== value.trim() || value.includes("\\") || path18.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized Native-relative ref`); + } + return value; +} +function positiveRevision(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native archive preflight revision must be a positive integer"); + } + return value; +} +function normalizeSpec(value, index) { + if (!NAME_PATTERN2.test(value.capability)) { + throw new Error(`Native archive spec ${index} capability is invalid`); + } + if (value.operation !== "create" && value.operation !== "replace" && value.operation !== "remove") { + throw new Error(`Native archive spec ${value.capability} operation is invalid`); + } + const expectedBaseHash = optionalHash( + value.expectedBaseHash, + `Native archive spec ${value.capability} expected base` + ); + const actualBaseHash = optionalHash( + value.actualBaseHash, + `Native archive spec ${value.capability} actual base` + ); + const proposedHash = optionalHash( + value.proposedHash, + `Native archive spec ${value.capability} proposed content` + ); + if (value.operation === "create" && expectedBaseHash !== null) { + throw new Error(`Native archive create ${value.capability} must expect no canonical base`); + } + if (value.operation !== "create" && expectedBaseHash === null) { + throw new Error(`Native archive ${value.operation} ${value.capability} requires a base hash`); + } + if (value.operation === "remove" !== (proposedHash === null)) { + throw new Error( + `Native archive ${value.operation} ${value.capability} proposed hash is invalid` + ); + } + return { + capability: value.capability, + operation: value.operation, + expectedBaseHash, + actualBaseHash, + proposedHash + }; +} +function normalizeEvidence(value) { + if (value.result !== "pending" && value.result !== "pass" && value.result !== "fail") { + throw new Error("Native archive verification result is invalid"); + } + if (!["missing", "invalid", "stale", "complete", "partial"].includes(value.freshness)) { + throw new Error("Native archive verification freshness is invalid"); + } + if (!Number.isSafeInteger(value.skippedAcceptanceCount) || value.skippedAcceptanceCount < 0) { + throw new Error("Native archive skipped acceptance count is invalid"); + } + const normalized = { + result: value.result, + freshness: value.freshness, + contractHash: optionalHash(value.contractHash, "Native archive contract hash"), + acceptanceHash: optionalHash(value.acceptanceHash, "Native archive acceptance hash"), + implementationScopeHash: optionalHash( + value.implementationScopeHash, + "Native archive implementation scope hash" + ), + reportHash: optionalHash(value.reportHash, "Native archive report hash"), + envelopeHash: optionalHash(value.envelopeHash, "Native archive envelope hash"), + partialAllowanceHash: optionalHash( + value.partialAllowanceHash, + "Native archive partial allowance hash" + ), + skippedAcceptanceCount: value.skippedAcceptanceCount + }; + if ((value.freshness === "complete" || value.freshness === "partial") && [ + normalized.contractHash, + normalized.acceptanceHash, + normalized.implementationScopeHash, + normalized.reportHash, + normalized.envelopeHash + ].some((entry2) => entry2 === null)) { + throw new Error("Fresh Native archive evidence requires every bound content hash"); + } + if (value.freshness === "partial" && normalized.partialAllowanceHash === null || value.freshness === "complete" && normalized.partialAllowanceHash !== null) { + throw new Error("Native archive partial evidence allowance state is invalid"); + } + return normalized; +} +function normalizeFindingCodes(values) { + if (values.length > 64) throw new Error("Native archive preflight has too many findings"); + const codes = values.map((value) => { + if (typeof value !== "string" || !FINDING_PATTERN.test(value)) { + throw new Error(`Native archive preflight finding code is invalid: ${String(value)}`); + } + return value; + }); + const normalized = [...new Set(codes)].sort(compareText); + if (normalized.length !== codes.length) { + throw new Error("Native archive preflight has duplicate finding codes"); + } + return normalized; +} +function derivedFindings(input) { + const findings = []; + if (input.phase !== "archive") findings.push("archive-phase-required"); + if (input.archived) findings.push("change-already-archived"); + if (input.pendingJournal) findings.push("pending-journal"); + if (input.targetExists) findings.push("archive-target-exists"); + if (input.evidence.result !== "pass") findings.push("verification-not-passed"); + if (input.evidence.freshness === "missing") findings.push("verification-evidence-missing"); + if (input.evidence.freshness === "invalid") findings.push("verification-evidence-invalid"); + if (input.evidence.freshness === "stale") findings.push("verification-evidence-stale"); + for (const spec of input.specs) { + if (spec.actualBaseHash !== spec.expectedBaseHash) findings.push("spec-base-conflict"); + } + return [...new Set(findings)].sort(compareText); +} +function buildNativeArchivePreflight(input) { + if (!NAME_PATTERN2.test(input.change)) throw new Error("Native archive change name is invalid"); + if (typeof input.stateSchema !== "string" || input.stateSchema.length === 0) { + throw new Error("Native archive state schema is invalid"); + } + if (!Array.isArray(input.specs) || input.specs.length > 64) { + throw new Error("Native archive preflight exceeds its spec budget"); + } + const specs = input.specs.map(normalizeSpec).sort((left, right) => compareText(left.capability, right.capability)); + if (new Set(specs.map((spec) => spec.capability)).size !== specs.length) { + throw new Error("Native archive preflight contains duplicate capabilities"); + } + const operations = specs.map((spec) => ({ + ...spec, + operationHash: canonicalHash(OPERATION_HASH_TAG, spec) + })); + const evidence = normalizeEvidence(input.evidence); + const findingCodes = [ + .../* @__PURE__ */ new Set([ + ...normalizeFindingCodes(input.findingCodes ?? []), + ...derivedFindings({ + phase: input.phase, + archived: input.archived, + pendingJournal: input.pendingJournal, + targetExists: input.targetExists, + specs, + evidence + }) + ]) + ].sort(compareText); + const revision = positiveRevision(input.revision); + const targetRef = normalizedRef(input.targetRef, "Native archive target"); + const facts = { + stateSchema: input.stateSchema, + change: input.change, + revision, + phase: input.phase, + archived: input.archived, + pendingJournal: input.pendingJournal, + targetRef, + targetExists: input.targetExists, + operations, + evidence, + findingCodes + }; + return { + schema: NATIVE_ARCHIVE_PREFLIGHT_SCHEMA, + change: input.change, + revision, + targetRef, + ready: findingCodes.length === 0, + evidenceFreshness: evidence.freshness, + operationCount: operations.length, + operations, + findingCodes, + preflightHash: canonicalHash(PREFLIGHT_HASH_TAG, facts) + }; +} + +// domains/comet-native/native-conflict-radar.ts +import { createHash as createHash10 } from "node:crypto"; +import path19 from "node:path"; +var NATIVE_CONFLICT_RADAR_SCHEMA = "comet.native.conflict-radar.v1"; +var NATIVE_CONFLICT_RADAR_LIMITS = Object.freeze({ + maxChanges: 32, + maxSpecsPerChange: 64, + maxArtifactsPerChange: 128, + maxTotalSpecs: 512, + maxTotalArtifacts: 1024, + maxSignalsPerRelationship: 8, + maxRelationships: 256, + maxSerializedBytes: 64 * 1024, + maxNameBytes: 128, + maxCapabilityBytes: 128, + maxArtifactPathBytes: 512 +}); +var RADAR_HASH_TAG = "comet.native.conflict-radar.v1"; +var SIGNAL_SET_HASH_TAG = "comet.native.conflict-radar-signals.v1"; +var HASH_PATTERN5 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN3 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CHANGE_KEYS2 = /* @__PURE__ */ new Set([ + "name", + "revision", + "specs", + "declaredArtifacts", + "workspaceIdentityHash" +]); +var SPEC_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "baseHash"]); +var ARTIFACT_KEYS = /* @__PURE__ */ new Set(["path", "kind"]); +function compareText2(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function byteLength(value) { + return Buffer.byteLength(value, "utf8"); +} +function strictRecord(value, keys, label) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + const record8 = value; + const unknown = Object.keys(record8).filter((key) => !keys.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + return record8; +} +function boundedText(value, label, maxBytes) { + if (typeof value !== "string" || value.length === 0 || value !== value.trim()) { + throw new Error(`${label} must be a non-empty trimmed string`); + } + if (byteLength(value) > maxBytes) throw new Error(`${label} exceeds its byte budget`); + return value; +} +function normalizedProjectPath(value, label) { + const candidate = boundedText(value, label, NATIVE_CONFLICT_RADAR_LIMITS.maxArtifactPathBytes); + const normalized = path19.posix.normalize(candidate); + if (candidate.includes("\\") || Array.from(candidate).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || /^(?:[A-Za-z]:|~)/u.test(candidate) || path19.posix.isAbsolute(normalized) || candidate.split("/").includes("..") || normalized !== candidate || normalized === "." || candidate.endsWith("/")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return candidate; +} +function normalizedHash(value, label) { + if (typeof value !== "string" || !HASH_PATTERN5.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function normalizeSpec2(value, label) { + const record8 = strictRecord(value, SPEC_KEYS, label); + const capability = boundedText( + record8.capability, + `${label} capability`, + NATIVE_CONFLICT_RADAR_LIMITS.maxCapabilityBytes + ); + if (!NAME_PATTERN3.test(capability)) throw new Error(`${label} capability is invalid`); + if (record8.operation !== "create" && record8.operation !== "replace" && record8.operation !== "remove") { + throw new Error(`${label} operation is invalid`); + } + const baseHash = record8.baseHash === null ? null : normalizedHash(record8.baseHash, `${label} canonical base hash`); + if (record8.operation === "create" && baseHash !== null) { + throw new Error(`${label} create operation requires a null canonical base hash`); + } + if (record8.operation !== "create" && baseHash === null) { + throw new Error(`${label} ${record8.operation} operation requires a canonical base hash`); + } + return { capability, operation: record8.operation, baseHash }; +} +function normalizeArtifact(value, label) { + const record8 = strictRecord(value, ARTIFACT_KEYS, label); + if (record8.kind !== "file" && record8.kind !== "directory") { + throw new Error(`${label} kind is invalid`); + } + return { path: normalizedProjectPath(record8.path, `${label} path`), kind: record8.kind }; +} +function normalizeChange(value, index) { + const record8 = strictRecord(value, CHANGE_KEYS2, `Conflict radar change ${index}`); + const name = boundedText( + record8.name, + `Conflict radar change ${index} name`, + NATIVE_CONFLICT_RADAR_LIMITS.maxNameBytes + ); + if (!NAME_PATTERN3.test(name)) throw new Error(`Conflict radar change ${index} name is invalid`); + if (!Number.isSafeInteger(record8.revision) || record8.revision < 1) { + throw new Error(`Conflict radar change ${name} revision must be a positive safe integer`); + } + if (!Array.isArray(record8.specs)) { + throw new Error(`Conflict radar change ${name} specs must be an array`); + } + if (record8.specs.length > NATIVE_CONFLICT_RADAR_LIMITS.maxSpecsPerChange) { + throw new Error(`Conflict radar change ${name} exceeds the spec budget`); + } + if (!Array.isArray(record8.declaredArtifacts)) { + throw new Error(`Conflict radar change ${name} declared artifacts must be an array`); + } + if (record8.declaredArtifacts.length > NATIVE_CONFLICT_RADAR_LIMITS.maxArtifactsPerChange) { + throw new Error(`Conflict radar change ${name} exceeds the declared artifact budget`); + } + const specs = record8.specs.map( + (spec, specIndex) => normalizeSpec2(spec, `Conflict radar change ${name} spec ${specIndex}`) + ).sort((left, right) => compareText2(left.capability, right.capability)); + if (new Set(specs.map((spec) => spec.capability)).size !== specs.length) { + throw new Error(`Conflict radar change ${name} has duplicate capabilities`); + } + const declaredArtifacts = record8.declaredArtifacts.map( + (artifact, artifactIndex) => normalizeArtifact(artifact, `Conflict radar change ${name} artifact ${artifactIndex}`) + ).sort( + (left, right) => compareText2(left.path, right.path) || compareText2(left.kind, right.kind) + ); + const artifactPaths = declaredArtifacts.map((artifact) => artifact.path); + if (new Set(artifactPaths).size !== artifactPaths.length) { + throw new Error(`Conflict radar change ${name} has duplicate or conflicting artifact paths`); + } + const workspaceIdentityHash = record8.workspaceIdentityHash === void 0 || record8.workspaceIdentityHash === null ? null : normalizedHash( + record8.workspaceIdentityHash, + `Conflict radar change ${name} workspace identity hash` + ); + return { + name, + revision: record8.revision, + specs, + declaredArtifacts, + workspaceIdentityHash + }; +} +function normalizeChanges(input) { + if (!Array.isArray(input)) throw new Error("Native conflict radar input must be an array"); + if (input.length > NATIVE_CONFLICT_RADAR_LIMITS.maxChanges) { + throw new Error("Native conflict radar exceeds the change budget"); + } + const changes = input.map(normalizeChange).sort((left, right) => compareText2(left.name, right.name)); + if (new Set(changes.map((change) => change.name)).size !== changes.length) { + throw new Error("Native conflict radar contains duplicate change names"); + } + const specCount = changes.reduce((total, change) => total + change.specs.length, 0); + if (specCount > NATIVE_CONFLICT_RADAR_LIMITS.maxTotalSpecs) { + throw new Error("Native conflict radar exceeds the total spec budget"); + } + const artifactCount = changes.reduce( + (total, change) => total + change.declaredArtifacts.length, + 0 + ); + if (artifactCount > NATIVE_CONFLICT_RADAR_LIMITS.maxTotalArtifacts) { + throw new Error("Native conflict radar exceeds the total declared artifact budget"); + } + return changes; +} +function capabilitySignal(left, right) { + const sameCanonicalBase = left.baseHash !== null && left.baseHash === right.baseHash; + const certainty = left.operation === "create" || right.operation === "create" || sameCanonicalBase ? "definite-conflict" : "possible-overlap"; + return { + kind: "capability", + certainty, + capability: left.capability, + leftOperation: left.operation, + rightOperation: right.operation, + leftBaseHash: left.baseHash, + rightBaseHash: right.baseHash + }; +} +function artifactOverlap(left, right) { + const leftOwnsRight = left.kind === "directory" && (right.path === left.path || right.path.startsWith(`${left.path}/`)); + const rightOwnsLeft = right.kind === "directory" && (left.path === right.path || left.path.startsWith(`${right.path}/`)); + const sameFile = left.kind === "file" && right.kind === "file" && left.path === right.path; + if (!leftOwnsRight && !rightOwnsLeft && !sameFile) return null; + return { + kind: "artifact", + certainty: sameFile ? "definite-conflict" : "possible-overlap", + leftArtifact: left, + rightArtifact: right + }; +} +function workspaceRelationship(left, right) { + if (left.workspaceIdentityHash === null || right.workspaceIdentityHash === null) return "unknown"; + return left.workspaceIdentityHash === right.workspaceIdentityHash ? "same" : "different"; +} +function relationship(left, right) { + const visibleSignals = []; + const signalHasher = createHash10("sha256").update(`${SIGNAL_SET_HASH_TAG} +[`); + let signalCount = 0; + let hasDefiniteConflict = false; + const addSignal = (signal) => { + if (signalCount > 0) signalHasher.update(","); + signalHasher.update(canonicalJson(signal)); + signalCount += 1; + if (signal.certainty === "definite-conflict") hasDefiniteConflict = true; + if (visibleSignals.length < NATIVE_CONFLICT_RADAR_LIMITS.maxSignalsPerRelationship) { + visibleSignals.push(signal); + } + }; + const rightSpecs = new Map(right.specs.map((spec) => [spec.capability, spec])); + for (const leftSpec of left.specs) { + const rightSpec = rightSpecs.get(leftSpec.capability); + if (rightSpec) addSignal(capabilitySignal(leftSpec, rightSpec)); + } + for (const leftArtifact of left.declaredArtifacts) { + for (const rightArtifact of right.declaredArtifacts) { + const signal = artifactOverlap(leftArtifact, rightArtifact); + if (signal) addSignal(signal); + } + } + const classification = hasDefiniteConflict ? "definite-conflict" : signalCount > 0 ? "possible-overlap" : "disjoint"; + signalHasher.update("]"); + return { + left: left.name, + right: right.name, + classification, + workspaceRelationship: workspaceRelationship(left, right), + signalCount, + signalHash: signalHasher.digest("hex"), + signals: visibleSignals, + signalsTruncated: visibleSignals.length !== signalCount + }; +} +function classificationRank(value) { + if (value === "definite-conflict") return 0; + if (value === "possible-overlap") return 1; + return 2; +} +function compareRelationships(left, right) { + return classificationRank(left.classification) - classificationRank(right.classification) || compareText2(left.left, right.left) || compareText2(left.right, right.right); +} +function snapshot(changes, allRelationships, relationships, radarHash) { + const counts = { + definiteConflict: allRelationships.filter( + ({ classification }) => classification === "definite-conflict" + ).length, + possibleOverlap: allRelationships.filter( + ({ classification }) => classification === "possible-overlap" + ).length, + disjoint: allRelationships.filter(({ classification }) => classification === "disjoint").length + }; + return { + schema: NATIVE_CONFLICT_RADAR_SCHEMA, + workspaceIdentityAdvisoryOnly: true, + changeCount: changes.length, + relationshipCount: allRelationships.length, + counts, + relationships, + relationshipsTruncated: relationships.length !== allRelationships.length, + omittedRelationshipCount: allRelationships.length - relationships.length, + radarHash + }; +} +function buildNativeConflictRadar(input) { + const changes = normalizeChanges(input); + const allRelationships = []; + for (let leftIndex = 0; leftIndex < changes.length; leftIndex += 1) { + for (let rightIndex = leftIndex + 1; rightIndex < changes.length; rightIndex += 1) { + allRelationships.push(relationship(changes[leftIndex], changes[rightIndex])); + } + } + allRelationships.sort(compareRelationships); + const hashRelationships = allRelationships.map((value) => ({ + left: value.left, + right: value.right, + classification: value.classification, + workspaceRelationship: value.workspaceRelationship, + signalCount: value.signalCount, + signalHash: value.signalHash + })); + const radarHash = canonicalHash(RADAR_HASH_TAG, { changes, relationships: hashRelationships }); + let relationships = allRelationships.slice(0, NATIVE_CONFLICT_RADAR_LIMITS.maxRelationships).map((value) => ({ ...value, signals: [...value.signals] })); + let result2 = snapshot(changes, allRelationships, relationships, radarHash); + while (relationships.length > 0 && byteLength(JSON.stringify(result2)) > NATIVE_CONFLICT_RADAR_LIMITS.maxSerializedBytes) { + relationships = relationships.slice(0, -1); + result2 = snapshot(changes, allRelationships, relationships, radarHash); + } + if (byteLength(JSON.stringify(result2)) > NATIVE_CONFLICT_RADAR_LIMITS.maxSerializedBytes) { + throw new Error("Native conflict radar summary exceeds its serialized output budget"); + } + return result2; +} + +// domains/comet-native/native-evidence-storage.ts +import { createHash as createHash12 } from "node:crypto"; +import { promises as fs15 } from "node:fs"; +import path22 from "node:path"; + +// domains/comet-native/native-verification-scope.ts +import { createHash as createHash11 } from "node:crypto"; +import path20 from "path"; +var NATIVE_IMPLEMENTATION_SCOPE_SCHEMA = "comet.native.implementation-scope.v2"; +var NATIVE_SNAPSHOT_PROJECTION_SCHEMA = "comet.native.content-snapshot-projection.v1"; +var SNAPSHOT_PROJECTION_HASH_TAG = "comet.native.content-snapshot-projection.v1"; +var IMPLEMENTATION_SCOPE_HASH_TAG = "comet.native.implementation-scope.v2"; +var UNRESOLVED_SCOPE_ID_TAG = "comet.native.unresolved-scope-id.v1"; +var SCOPE_DETAIL_OVERFLOW_HASH_TAG = "comet.native.scope-detail-overflow.v1"; +var SNAPSHOT_PROJECTION_OVERFLOW_HASH_TAG = "comet.native.snapshot-projection-overflow.v2"; +var SHA256_HASH_PATTERN = /^[a-f0-9]{64}$/u; +var MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES = 1024 * 1024; +var MAX_NATIVE_DETAILED_SCOPE_CHANGES = 128; +var MAX_NATIVE_DETAILED_UNRESOLVED_SCOPES = 128; +function compareText3(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function nativeSnapshotProjectionRef(hash6) { + if (!SHA256_HASH_PATTERN.test(hash6)) { + throw new Error("Native snapshot projection hash must be a SHA-256 hash"); + } + return `runtime/evidence/snapshots/${hash6}.json`; +} +function compareNullableText(left, right) { + if (left === right) return 0; + if (left === null) return -1; + if (right === null) return 1; + return compareText3(left, right); +} +function projectRelativePath(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.endsWith("/") || /^[a-zA-Z]:/u.test(value)) { + throw new Error(`${label} must be a normalized project-relative path`); + } + const normalized = path20.posix.normalize(value); + if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../") || path20.posix.isAbsolute(normalized)) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function snapshotOmissionPath(value, label) { + return value === "." ? value : projectRelativePath(value, label); +} +function nonNegativeSafeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative safe integer`); + } + return value; +} +function normalizeEntry(entry2, label) { + if (entry2.type !== "file") throw new Error(`${label} must describe a file`); + if (typeof entry2.hash !== "string" || entry2.hash.length === 0) { + throw new Error(`${label} hash must be non-empty`); + } + return { + path: projectRelativePath(entry2.path, `${label} path`), + hash: entry2.hash, + size: nonNegativeSafeInteger(entry2.size, `${label} size`), + type: "file" + }; +} +function normalizeOmission(omission, label) { + return { + path: snapshotOmissionPath(omission.path, `${label} path`), + size: omission.size === null ? null : nonNegativeSafeInteger(omission.size, `${label} size`), + type: omission.type, + reason: omission.reason + }; +} +function compareEntries(left, right) { + return compareText3(left.path, right.path) || compareText3(left.hash, right.hash) || left.size - right.size; +} +function compareOmissions(left, right) { + return compareText3(left.path, right.path) || compareText3(left.reason, right.reason) || compareText3(left.type, right.type) || (left.size ?? -1) - (right.size ?? -1); +} +function serializedEvidenceBytes(value) { + return Buffer.byteLength(JSON.stringify(value, null, 2) + "\n", "utf8"); +} +function updateFramedHash(hash6, kind, value) { + const encoded = `${kind} +${canonicalJson(value)}`; + hash6.update(`${Buffer.byteLength(encoded, "utf8")}:`); + hash6.update(encoded); +} +function fitSnapshotProjectionBudget(value) { + if (serializedEvidenceBytes(value) <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + return value; + } + const entries = [...value.entries]; + const omitted = [...value.omitted]; + let omittedCount = value.omittedCount; + let overflowCount = value.omissionOverflow?.count ?? 0; + const overflowHash = createHash11("sha256"); + overflowHash.update(`${SNAPSHOT_PROJECTION_OVERFLOW_HASH_TAG} +`); + if (value.omissionOverflow) { + updateFramedHash(overflowHash, "existing-overflow", value.omissionOverflow); + } + const foldOmission = (omission, alreadyCounted) => { + if (!alreadyCounted) omittedCount += 1; + overflowCount += 1; + updateFramedHash(overflowHash, "omission", omission); + }; + const foldEntry = (entry2) => { + omittedCount += 1; + overflowCount += 1; + updateFramedHash(overflowHash, "entry", entry2); + }; + const takeCompactableOmission = () => { + for (let index = omitted.length - 1; index >= 0; index -= 1) { + const omission = omitted[index]; + if (omission.reason === "git-enumeration-limit" || omission.reason === "git-selection-changed" || omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed") { + continue; + } + omitted.splice(index, 1); + return omission; + } + return null; + }; + const candidate = () => ({ + ...value, + complete: omittedCount === 0, + entries, + omitted, + omittedCount, + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${"0".repeat(64)}`, + hash: "0".repeat(64), + count: overflowCount + } + }); + let projection = candidate(); + while (serializedEvidenceBytes(projection) > MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + const compactableOmissionCount = omitted.filter( + (omission) => omission.reason !== "git-enumeration-limit" && omission.reason !== "git-selection-changed" && omission.reason !== "physical-enumeration-limit" && omission.reason !== "physical-selection-changed" + ).length; + if (compactableOmissionCount > 0) { + const removeCount = Math.max(1, Math.ceil(omitted.length / 4)); + for (let removed = 0; removed < removeCount; removed += 1) { + const omission = takeCompactableOmission(); + if (omission === null) break; + foldOmission(omission, true); + } + } else if (entries.length > 0) { + const removeCount = Math.max(1, Math.ceil(entries.length / 4)); + for (const entry2 of entries.splice(-removeCount)) { + foldEntry(entry2); + } + } else { + throw new Error("Native snapshot projection metadata exceeds its evidence byte budget"); + } + projection = candidate(); + } + const digest = overflowHash.digest("hex"); + return { + ...projection, + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${digest}`, + hash: digest, + count: overflowCount + } + }; +} +function snapshotProjection(manifest) { + const parsed = parseNativeContentSnapshotManifest(manifest); + const entries = parsed.entries.map( + (entry2, index) => normalizeEntry(entry2, `Snapshot entry ${index}`) + ); + entries.sort(compareEntries); + if (new Set(entries.map((entry2) => entry2.path)).size !== entries.length) { + throw new Error("Snapshot entries must not contain duplicate paths"); + } + const omitted = parsed.omitted.map( + (omission, index) => normalizeOmission(omission, `Snapshot omission ${index}`) + ); + omitted.sort(compareOmissions); + return fitSnapshotProjectionBudget({ + schema: NATIVE_SNAPSHOT_PROJECTION_SCHEMA, + origin: parsed.origin, + ...parsed.capture ? { capture: parsed.capture } : {}, + complete: parsed.complete, + limits: { + maxFiles: parsed.limits.maxFiles, + maxFileBytes: parsed.limits.maxFileBytes, + maxTotalBytes: parsed.limits.maxTotalBytes, + maxManifestBytes: parsed.limits.maxManifestBytes, + ...parsed.limits.maxDurationMs === void 0 ? {} : { maxDurationMs: parsed.limits.maxDurationMs } + }, + ...parsed.policy ? { policy: parsed.policy } : {}, + entries, + omitted, + omittedCount: parsed.omittedCount, + ...parsed.omissionOverflow ? { + omissionOverflow: { + ref: parsed.omissionOverflow.ref, + hash: parsed.omissionOverflow.hash, + count: parsed.omissionOverflow.count + } + } : {} + }); +} +function normalizeDeclaredArtifacts(artifacts) { + const byPath = /* @__PURE__ */ new Map(); + for (const [index, artifact] of artifacts.entries()) { + if (artifact.kind !== "file" && artifact.kind !== "directory") { + throw new Error(`Declared artifact ${index} kind is invalid`); + } + const normalized = { + path: projectRelativePath(artifact.path, `Declared artifact ${index} path`), + kind: artifact.kind + }; + const existing = byPath.get(normalized.path); + if (existing && existing.kind !== normalized.kind) { + throw new Error(`Declared artifact path has conflicting kinds: ${normalized.path}`); + } + byPath.set(normalized.path, normalized); + } + return [...byPath.values()].sort( + (left, right) => compareText3(left.path, right.path) || compareText3(left.kind, right.kind) + ); +} +function artifactOwnsPath(artifact, changedPath) { + if (artifact.kind === "file") return artifact.path === changedPath; + return changedPath === artifact.path || changedPath.startsWith(`${artifact.path}/`); +} +function fileIdentity(entry2) { + return entry2 ? { hash: entry2.hash, size: entry2.size } : null; +} +function visitDerivedChanges(baseline, current, visitor) { + let beforeIndex = 0; + let afterIndex = 0; + while (beforeIndex < baseline.entries.length || afterIndex < current.entries.length) { + const beforeCandidate = baseline.entries[beforeIndex]; + const afterCandidate = current.entries[afterIndex]; + const order = beforeCandidate === void 0 ? 1 : afterCandidate === void 0 ? -1 : compareText3(beforeCandidate.path, afterCandidate.path); + const before = order <= 0 ? beforeCandidate : void 0; + const after = order >= 0 ? afterCandidate : void 0; + if (order <= 0) beforeIndex += 1; + if (order >= 0) afterIndex += 1; + if (before && after && before.hash === after.hash && before.size === after.size) continue; + if (before && !after && !current.complete) continue; + const changedPath = before?.path ?? after?.path; + if (changedPath === void 0) continue; + visitor({ + path: changedPath, + kind: before ? after ? "modified" : "removed" : "added", + before: fileIdentity(before), + after: fileIdentity(after) + }); + } +} +function materializeChange(core, declaredArtifacts) { + return { + ...core, + attributedTo: declaredArtifacts.filter((artifact) => artifactOwnsPath(artifact, core.path)) + }; +} +function unresolvedScope(identity, reason) { + return { + id: `scope:${canonicalHash(UNRESOLVED_SCOPE_ID_TAG, identity)}`, + kind: identity.kind, + source: identity.source, + path: identity.path, + reason + }; +} +function omissionScopes(source, projection) { + const scopes = projection.omitted.map( + (omission) => unresolvedScope( + { + kind: "snapshot-omission", + source, + path: omission.path, + evidence: { + reason: omission.reason, + size: omission.size, + type: omission.type + } + }, + `${source} snapshot omitted ${omission.path}: ${omission.reason}` + ) + ); + if (!projection.complete) { + scopes.push( + unresolvedScope( + { + kind: "snapshot-incomplete", + source, + path: null, + evidence: { omittedCount: projection.omittedCount } + }, + `${source} snapshot is incomplete` + ) + ); + } + if (projection.omissionOverflow) { + scopes.push( + unresolvedScope( + { + kind: "snapshot-omission-overflow", + source, + path: null, + evidence: { + count: projection.omissionOverflow.count, + hash: projection.omissionOverflow.hash, + ref: projection.omissionOverflow.ref + } + }, + `${source} snapshot has ${projection.omissionOverflow.count} unlisted omissions` + ) + ); + } + return scopes; +} +function omissionSummaryScopes(source, projection) { + return omissionScopes(source, { ...projection, omitted: [] }).filter( + (scope) => scope.kind !== "snapshot-omission" + ); +} +function compareUnresolvedScopes(left, right) { + return compareText3(left.kind, right.kind) || compareText3(left.source, right.source) || compareNullableText(left.path, right.path) || compareText3(left.id, right.id); +} +function uniqueUnresolvedScopes(scopes) { + const byId = new Map(scopes.map((scope) => [scope.id, scope])); + return [...byId.values()].sort(compareUnresolvedScopes); +} +function normalizeGitChangedPaths(paths) { + return [ + ...new Set(paths.map((value, index) => projectRelativePath(value, `Git path ${index}`))) + ].sort(compareText3); +} +function normalizeScopeAuthority(input) { + if (typeof input.contractHash !== "string" || !SHA256_HASH_PATTERN.test(input.contractHash)) { + throw new Error("Contract hash must be a SHA-256 hash"); + } + return { + contractHash: input.contractHash, + declaredArtifacts: normalizeDeclaredArtifacts(input.declaredArtifacts), + noCodeReason: input.noCodeReason?.trim() || null, + ...input.gitChangedPaths === void 0 ? {} : { gitChangedPaths: normalizeGitChangedPaths(input.gitChangedPaths) } + }; +} +function scanScopeChanges(baseline, current, declaredArtifacts, gitChangedPaths) { + const candidates = []; + const gitChangedPathSet = gitChangedPaths === void 0 ? null : new Set(gitChangedPaths); + const gitPathsPresentInChanges = []; + let candidateBytes = 0; + let candidateBudgetExhausted = false; + let totalChanges = 0; + let totalUnattributed = 0; + visitDerivedChanges(baseline, current, (core) => { + const attributed = declaredArtifacts.some((artifact) => artifactOwnsPath(artifact, core.path)); + totalChanges += 1; + if (!attributed) totalUnattributed += 1; + if (gitChangedPathSet?.has(core.path)) gitPathsPresentInChanges.push(core.path); + if (!candidateBudgetExhausted && candidates.length < MAX_NATIVE_DETAILED_SCOPE_CHANGES) { + const candidate = materializeChange(core, declaredArtifacts); + const nextBytes = serializedEvidenceBytes(candidate); + if (candidateBytes + nextBytes <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + candidates.push(candidate); + candidateBytes += nextBytes; + } else { + candidateBudgetExhausted = true; + } + } + }); + return { candidates, gitPathsPresentInChanges, totalChanges, totalUnattributed }; +} +function scopeDetailOverflow(hash6, counts) { + return unresolvedScope( + { + kind: "scope-detail-overflow", + source: "implementation-scope", + path: null, + evidence: { + changeCount: counts.changes, + hash: hash6, + unattributedCount: counts.unattributed, + unresolvedCount: counts.unresolved + } + }, + `Implementation scope summarized ${counts.changes} additional change details and ${counts.unresolved} unresolved details (${counts.unattributed} unattributed); overflow hash ${hash6}` + ); +} +function parseScopeDetailOverflow(scope) { + const match = /^Implementation scope summarized ([0-9]+) additional change details and ([0-9]+) unresolved details \(([0-9]+) unattributed\); overflow hash ([a-f0-9]{64})$/u.exec( + scope.reason + ); + if (!match) throw new Error("Implementation scope detail overflow is invalid"); + const counts = { + changes: nonNegativeSafeInteger(Number(match[1]), "Scope overflow change count"), + unresolved: nonNegativeSafeInteger(Number(match[2]), "Scope overflow unresolved count"), + unattributed: nonNegativeSafeInteger(Number(match[3]), "Scope overflow unattributed count") + }; + const hash6 = scopeHashValue(match[4], "Scope detail overflow hash"); + if (JSON.stringify(scope) !== JSON.stringify(scopeDetailOverflow(hash6, counts))) { + throw new Error("Implementation scope detail overflow is inconsistent"); + } + return { ...counts, hash: hash6 }; +} +function hashScopeDetailOverflow(options) { + const hash6 = createHash11("sha256"); + hash6.update(`${SCOPE_DETAIL_OVERFLOW_HASH_TAG} +`); + let changeIndex = 0; + visitDerivedChanges(options.baseline, options.current, (core) => { + if (changeIndex >= options.detailedChangeCount) { + updateFramedHash(hash6, "change", core); + let attributionCount = 0; + for (const artifact of options.declaredArtifacts) { + if (!artifactOwnsPath(artifact, core.path)) continue; + attributionCount += 1; + updateFramedHash(hash6, "change-attribution", artifact); + } + updateFramedHash(hash6, "change-end", { attributionCount }); + if (attributionCount === 0) { + updateFramedHash(hash6, "unattributed-change", { + after: core.after, + before: core.before, + changeKind: core.kind, + path: core.path + }); + } + } + changeIndex += 1; + }); + let omissionIndex = 0; + for (const [source, projection] of [ + ["baseline", options.baseline], + ["current", options.current] + ]) { + for (const omission of projection.omitted) { + if (omissionIndex >= options.detailedOmissionCount) { + updateFramedHash(hash6, "snapshot-omission", { source, ...omission }); + } + omissionIndex += 1; + } + } + return hash6.digest("hex"); +} +function buildScopeFromProjections(baseline, current, authority) { + const { contractHash, declaredArtifacts, noCodeReason } = authority; + const changeScan = scanScopeChanges( + baseline, + current, + declaredArtifacts, + authority.gitChangedPaths + ); + const baselineProjectionHash = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, baseline); + const currentProjectionHash2 = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, current); + const omissionCandidates = []; + for (const [source, projection] of [ + ["baseline", baseline], + ["current", current] + ]) { + for (const omission of projection.omitted) { + if (omissionCandidates.length >= MAX_NATIVE_DETAILED_UNRESOLVED_SCOPES) break; + omissionCandidates.push( + unresolvedScope( + { + kind: "snapshot-omission", + source, + path: omission.path, + evidence: { + reason: omission.reason, + size: omission.size, + type: omission.type + } + }, + `${source} snapshot omitted ${omission.path}: ${omission.reason}` + ) + ); + } + } + const totalOmissionDetails = baseline.omitted.length + current.omitted.length; + const essentialScopes = [ + ...omissionSummaryScopes("baseline", baseline), + ...omissionSummaryScopes("current", current), + ...changeScan.totalChanges === 0 && noCodeReason === null ? [ + unresolvedScope( + { + kind: "missing-no-code-reason", + source: "implementation-scope", + path: null, + evidence: { + baselineProjectionHash, + currentProjectionHash: currentProjectionHash2 + } + }, + "A non-empty no-code reason is required when the snapshots contain no changes" + ) + ] : [] + ]; + const buildGitAdvisory = () => { + if (authority.gitChangedPaths === void 0) return void 0; + const snapshotChangePaths = new Set(changeScan.gitPathsPresentInChanges); + return { + advisoryOnly: true, + changedPaths: authority.gitChangedPaths, + pathsPresentInSnapshotChanges: authority.gitChangedPaths.filter( + (value) => snapshotChangePaths.has(value) + ), + pathsAbsentFromSnapshotChanges: authority.gitChangedPaths.filter( + (value) => !snapshotChangePaths.has(value) + ) + }; + }; + const buildScopeContent = (options) => { + const changes = changeScan.candidates.slice(0, options.detailedChangeCount); + const unattributed = changes.filter((change) => change.attributedTo.length === 0); + const overflowChanges = changeScan.totalChanges - changes.length; + const overflowUnattributed = changeScan.totalUnattributed - unattributed.length; + const overflowOmissions = totalOmissionDetails - options.detailedOmissionCount; + const overflowUnresolved = overflowUnattributed + overflowOmissions; + const unresolved = [ + ...unattributed.map( + (change) => unresolvedScope( + { + kind: "unattributed-change", + source: "implementation-scope", + path: change.path, + evidence: { + after: change.after, + before: change.before, + changeKind: change.kind + } + }, + `Changed path is not covered by a declared artifact: ${change.path}` + ) + ), + ...omissionCandidates.slice(0, options.detailedOmissionCount), + ...essentialScopes, + ...overflowChanges > 0 || overflowUnresolved > 0 ? [ + scopeDetailOverflow(options.overflowHash, { + changes: overflowChanges, + unattributed: overflowUnattributed, + unresolved: overflowUnresolved + }) + ] : [] + ]; + const unresolvedScopes = uniqueUnresolvedScopes(unresolved); + const gitAdvisory = options.includeGitAdvisory ? buildGitAdvisory() : void 0; + return { + schema: NATIVE_IMPLEMENTATION_SCOPE_SCHEMA, + contractHash, + baselineProjectionRef: nativeSnapshotProjectionRef(baselineProjectionHash), + baselineProjectionHash, + currentProjectionRef: nativeSnapshotProjectionRef(currentProjectionHash2), + currentProjectionHash: currentProjectionHash2, + complete: unresolvedScopes.length === 0, + declaredArtifacts, + changes, + unattributed, + unresolvedScopes, + noCodeReason, + ...gitAdvisory ? { gitAdvisory } : {} + }; + }; + let detailedChangeCount = changeScan.candidates.length; + let detailedOmissionCount = omissionCandidates.length; + let includeGitAdvisory = authority.gitChangedPaths !== void 0; + const placeholderHash = "0".repeat(64); + while (true) { + const candidate = buildScopeContent({ + detailedChangeCount, + detailedOmissionCount, + includeGitAdvisory, + overflowHash: placeholderHash + }); + if (serializedEvidenceBytes({ ...candidate, scopeHash: placeholderHash }) <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + break; + } + if (includeGitAdvisory) { + includeGitAdvisory = false; + continue; + } + if (detailedOmissionCount > 0) { + detailedOmissionCount -= Math.max(1, Math.ceil(detailedOmissionCount / 4)); + continue; + } + if (detailedChangeCount > 0) { + const removable = detailedChangeCount; + detailedChangeCount -= Math.max(1, Math.ceil(removable / 4)); + continue; + } + throw new Error("Native implementation scope metadata exceeds its evidence byte budget"); + } + const hasOverflow = changeScan.totalChanges > detailedChangeCount || totalOmissionDetails > detailedOmissionCount; + const overflowHash = hasOverflow ? hashScopeDetailOverflow({ + baseline, + current, + declaredArtifacts, + detailedChangeCount, + detailedOmissionCount + }) : placeholderHash; + const scopeContent = buildScopeContent({ + detailedChangeCount, + detailedOmissionCount, + includeGitAdvisory, + overflowHash + }); + const scope = { + ...scopeContent, + scopeHash: canonicalHash(IMPLEMENTATION_SCOPE_HASH_TAG, scopeContent) + }; + if (serializedEvidenceBytes(scope) > MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + throw new Error("Native implementation scope exceeded its evidence byte budget after fitting"); + } + return scope; +} +function buildNativeImplementationScopeBundle(input) { + const baseline = snapshotProjection(input.baseline); + const current = snapshotProjection(input.current); + const authority = normalizeScopeAuthority(input); + return { + authority, + baseline, + current, + scope: buildScopeFromProjections(baseline, current, authority) + }; +} +var SCOPE_KEYS = /* @__PURE__ */ new Set([ + "schema", + "contractHash", + "baselineProjectionRef", + "baselineProjectionHash", + "currentProjectionRef", + "currentProjectionHash", + "complete", + "declaredArtifacts", + "changes", + "unattributed", + "unresolvedScopes", + "noCodeReason", + "gitAdvisory", + "scopeHash" +]); +function scopeRecord(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactScopeKeys(value, required, optional, label) { + const allowed = /* @__PURE__ */ new Set([...required, ...optional]); + const unknown = Object.keys(value).filter((key) => !allowed.has(key)); + const missing = required.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function scopeHashValue(value, label) { + if (typeof value !== "string" || !SHA256_HASH_PATTERN.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function snapshotProjectionRefValue(value, hash6, label) { + const expected = nativeSnapshotProjectionRef(hash6); + if (value !== expected) throw new Error(`${label} ref/hash mismatch`); + return expected; +} +function parseNativeSnapshotProjection(value, expectedHash) { + const root = scopeRecord(value, "Native snapshot projection"); + exactScopeKeys( + root, + ["schema", "origin", "complete", "limits", "entries", "omitted", "omittedCount"], + ["capture", "omissionOverflow", "policy"], + "Native snapshot projection" + ); + if (root.schema !== NATIVE_SNAPSHOT_PROJECTION_SCHEMA) { + throw new Error("Native snapshot projection schema is invalid"); + } + const parsedManifest = parseNativeContentSnapshotManifest({ + schema: "comet.native.content-snapshot.v1", + origin: root.origin, + ...root.capture === void 0 ? {} : { capture: root.capture }, + createdAt: "1970-01-01T00:00:00.000Z", + complete: root.complete, + limits: root.limits, + ...root.policy === void 0 ? {} : { policy: root.policy }, + entries: root.entries, + omitted: root.omitted, + omittedCount: root.omittedCount, + ...root.omissionOverflow === void 0 ? {} : { omissionOverflow: root.omissionOverflow } + }); + const projection = snapshotProjection(parsedManifest); + if (canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, root) !== canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, projection)) { + throw new Error("Native snapshot projection is not canonical"); + } + const projectionHash = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, projection); + if (expectedHash !== void 0 && scopeHashValue(expectedHash, "Snapshot projection hash") !== projectionHash) { + throw new Error("Native snapshot projection content hash mismatch"); + } + return projection; +} +function parseDeclaredArtifact(value, index) { + const artifact = scopeRecord(value, `Declared artifact ${index}`); + exactScopeKeys(artifact, ["path", "kind"], [], `Declared artifact ${index}`); + if (artifact.kind !== "file" && artifact.kind !== "directory") { + throw new Error(`Declared artifact ${index} kind is invalid`); + } + return { + path: projectRelativePath(artifact.path, `Declared artifact ${index} path`), + kind: artifact.kind + }; +} +function parseFileIdentity(value, label) { + if (value === null) return null; + const identity = scopeRecord(value, label); + exactScopeKeys(identity, ["hash", "size"], [], label); + return { + hash: scopeHashValue(identity.hash, `${label} hash`), + size: nonNegativeSafeInteger(identity.size, `${label} size`) + }; +} +function parseImplementationChange(value, index) { + const change = scopeRecord(value, `Implementation change ${index}`); + exactScopeKeys( + change, + ["path", "kind", "before", "after", "attributedTo"], + [], + `Implementation change ${index}` + ); + if (change.kind !== "added" && change.kind !== "modified" && change.kind !== "removed") { + throw new Error(`Implementation change ${index} kind is invalid`); + } + if (!Array.isArray(change.attributedTo)) { + throw new Error(`Implementation change ${index} attributedTo must be an array`); + } + const attributedTo = change.attributedTo.map(parseDeclaredArtifact); + const normalizedAttribution = normalizeDeclaredArtifacts(attributedTo); + if (JSON.stringify(attributedTo) !== JSON.stringify(normalizedAttribution)) { + throw new Error(`Implementation change ${index} attribution must be sorted and unique`); + } + const before = parseFileIdentity(change.before, `Implementation change ${index} before`); + const after = parseFileIdentity(change.after, `Implementation change ${index} after`); + if (change.kind === "added" && (before !== null || after === null) || change.kind === "removed" && (before === null || after !== null) || change.kind === "modified" && (before === null || after === null || JSON.stringify(before) === JSON.stringify(after))) { + throw new Error(`Implementation change ${index} before/after state is invalid`); + } + return { + path: projectRelativePath(change.path, `Implementation change ${index} path`), + kind: change.kind, + before, + after, + attributedTo + }; +} +function parseUnresolvedScope(value, index) { + const scope = scopeRecord(value, `Unresolved scope ${index}`); + exactScopeKeys( + scope, + ["id", "kind", "source", "path", "reason"], + [], + `Unresolved scope ${index}` + ); + const kinds = /* @__PURE__ */ new Set([ + "unattributed-change", + "snapshot-omission", + "snapshot-incomplete", + "snapshot-omission-overflow", + "scope-detail-overflow", + "missing-no-code-reason" + ]); + if (typeof scope.kind !== "string" || !kinds.has(scope.kind)) { + throw new Error(`Unresolved scope ${index} kind is invalid`); + } + if (scope.source !== "baseline" && scope.source !== "current" && scope.source !== "implementation-scope") { + throw new Error(`Unresolved scope ${index} source is invalid`); + } + if (typeof scope.id !== "string" || !/^scope:[a-f0-9]{64}$/u.test(scope.id)) { + throw new Error(`Unresolved scope ${index} id is invalid`); + } + if (scope.path !== null && typeof scope.path !== "string") { + throw new Error(`Unresolved scope ${index} path is invalid`); + } + if (typeof scope.reason !== "string" || scope.reason.length === 0 || scope.reason.trim() !== scope.reason) { + throw new Error(`Unresolved scope ${index} reason is invalid`); + } + return { + id: scope.id, + kind: scope.kind, + source: scope.source, + path: scope.path === null ? null : scope.kind === "snapshot-omission" ? snapshotOmissionPath(scope.path, `Unresolved scope ${index} path`) : projectRelativePath(scope.path, `Unresolved scope ${index} path`), + reason: scope.reason + }; +} +function parseSortedPaths(value, label) { + if (!Array.isArray(value) || value.some((entry2) => typeof entry2 !== "string")) { + throw new Error(`${label} must be an array of paths`); + } + const paths = value.map((entry2, index) => projectRelativePath(entry2, `${label} ${index}`)); + const normalized = [...new Set(paths)].sort(compareText3); + if (JSON.stringify(paths) !== JSON.stringify(normalized)) { + throw new Error(`${label} must be sorted and unique`); + } + return paths; +} +function parseNativeImplementationScope(value) { + const root = scopeRecord(value, "Native implementation scope"); + const required = [...SCOPE_KEYS].filter((key) => key !== "gitAdvisory"); + exactScopeKeys(root, required, ["gitAdvisory"], "Native implementation scope"); + if (root.schema !== NATIVE_IMPLEMENTATION_SCOPE_SCHEMA) { + throw new Error("Native implementation scope schema is invalid"); + } + const contractHash = scopeHashValue(root.contractHash, "Implementation scope contractHash"); + const baselineProjectionHash = scopeHashValue( + root.baselineProjectionHash, + "Implementation scope baselineProjectionHash" + ); + const baselineProjectionRef = snapshotProjectionRefValue( + root.baselineProjectionRef, + baselineProjectionHash, + "Implementation scope baseline projection" + ); + const currentProjectionHash2 = scopeHashValue( + root.currentProjectionHash, + "Implementation scope currentProjectionHash" + ); + const currentProjectionRef = snapshotProjectionRefValue( + root.currentProjectionRef, + currentProjectionHash2, + "Implementation scope current projection" + ); + if (typeof root.complete !== "boolean") { + throw new Error("Implementation scope complete flag is invalid"); + } + if (!Array.isArray(root.declaredArtifacts) || !Array.isArray(root.changes) || !Array.isArray(root.unattributed) || !Array.isArray(root.unresolvedScopes)) { + throw new Error("Implementation scope collections are invalid"); + } + const declaredArtifacts = root.declaredArtifacts.map(parseDeclaredArtifact); + if (JSON.stringify(declaredArtifacts) !== JSON.stringify(normalizeDeclaredArtifacts(declaredArtifacts))) { + throw new Error("Implementation scope declared artifacts must be sorted and unique"); + } + const changes = root.changes.map(parseImplementationChange); + const sortedChanges = [...changes].sort((left, right) => compareText3(left.path, right.path)); + if (JSON.stringify(changes) !== JSON.stringify(sortedChanges) || new Set(changes.map((change) => change.path)).size !== changes.length) { + throw new Error("Implementation scope changes must be sorted and unique"); + } + const declaredByIdentity = new Set( + declaredArtifacts.map((artifact) => `${artifact.kind}:${artifact.path}`) + ); + if (changes.some( + (change) => change.attributedTo.some( + (artifact) => !declaredByIdentity.has(`${artifact.kind}:${artifact.path}`) + ) + )) { + throw new Error("Implementation scope change references an undeclared artifact"); + } + if (changes.some( + (change) => JSON.stringify(change.attributedTo) !== JSON.stringify( + declaredArtifacts.filter((artifact) => artifactOwnsPath(artifact, change.path)) + ) + )) { + throw new Error("Implementation scope change attribution is inconsistent"); + } + const unattributed = root.unattributed.map(parseImplementationChange); + const expectedUnattributed = changes.filter((change) => change.attributedTo.length === 0); + if (JSON.stringify(unattributed) !== JSON.stringify(expectedUnattributed)) { + throw new Error("Implementation scope unattributed changes are inconsistent"); + } + const unresolvedScopes = root.unresolvedScopes.map(parseUnresolvedScope); + const detailOverflowScopes = unresolvedScopes.filter( + (scope) => scope.kind === "scope-detail-overflow" + ); + if (detailOverflowScopes.length > 1) { + throw new Error("Implementation scope has multiple detail overflow records"); + } + const detailOverflow = detailOverflowScopes[0] === void 0 ? null : parseScopeDetailOverflow(detailOverflowScopes[0]); + if (detailOverflow && (detailOverflow.changes + detailOverflow.unresolved === 0 || detailOverflow.unattributed > detailOverflow.changes || detailOverflow.unattributed > detailOverflow.unresolved)) { + throw new Error("Implementation scope detail overflow counts are inconsistent"); + } + const noCodeReason = root.noCodeReason; + const expectedDerivedScopes = [ + ...expectedUnattributed.map( + (change) => unresolvedScope( + { + kind: "unattributed-change", + source: "implementation-scope", + path: change.path, + evidence: { + after: change.after, + before: change.before, + changeKind: change.kind + } + }, + `Changed path is not covered by a declared artifact: ${change.path}` + ) + ), + ...changes.length + (detailOverflow?.changes ?? 0) === 0 && noCodeReason === null ? [ + unresolvedScope( + { + kind: "missing-no-code-reason", + source: "implementation-scope", + path: null, + evidence: { baselineProjectionHash, currentProjectionHash: currentProjectionHash2 } + }, + "A non-empty no-code reason is required when the snapshots contain no changes" + ) + ] : [] + ]; + const actualDerivedScopes = unresolvedScopes.filter( + (scope) => scope.kind === "unattributed-change" || scope.kind === "missing-no-code-reason" + ); + if (JSON.stringify(unresolvedScopes) !== JSON.stringify([...unresolvedScopes].sort(compareUnresolvedScopes)) || new Set(unresolvedScopes.map((scope) => scope.id)).size !== unresolvedScopes.length || JSON.stringify(actualDerivedScopes) !== JSON.stringify(expectedDerivedScopes.sort(compareUnresolvedScopes)) || root.complete !== (unresolvedScopes.length === 0)) { + throw new Error("Implementation scope unresolved scopes are inconsistent"); + } + if (root.noCodeReason !== null && (typeof root.noCodeReason !== "string" || root.noCodeReason.length === 0 || root.noCodeReason.trim() !== root.noCodeReason)) { + throw new Error("Implementation scope no-code reason is invalid"); + } + let gitAdvisory; + if (root.gitAdvisory !== void 0) { + const advisory = scopeRecord(root.gitAdvisory, "Implementation scope Git advisory"); + exactScopeKeys( + advisory, + [ + "advisoryOnly", + "changedPaths", + "pathsPresentInSnapshotChanges", + "pathsAbsentFromSnapshotChanges" + ], + [], + "Implementation scope Git advisory" + ); + if (advisory.advisoryOnly !== true) { + throw new Error("Implementation scope Git advisory must remain advisory-only"); + } + const changedPaths = parseSortedPaths(advisory.changedPaths, "Git changed paths"); + const pathsPresentInSnapshotChanges = parseSortedPaths( + advisory.pathsPresentInSnapshotChanges, + "Git present paths" + ); + const pathsAbsentFromSnapshotChanges = parseSortedPaths( + advisory.pathsAbsentFromSnapshotChanges, + "Git absent paths" + ); + const partition = [...pathsPresentInSnapshotChanges, ...pathsAbsentFromSnapshotChanges].sort( + compareText3 + ); + const detailedChangePaths = new Set(changes.map((change) => change.path)); + if (JSON.stringify(partition) !== JSON.stringify(changedPaths) || (detailOverflow?.changes ?? 0) === 0 && pathsPresentInSnapshotChanges.some((entry2) => !detailedChangePaths.has(entry2)) || pathsAbsentFromSnapshotChanges.some((entry2) => detailedChangePaths.has(entry2))) { + throw new Error("Implementation scope Git advisory partition is invalid"); + } + gitAdvisory = { + advisoryOnly: true, + changedPaths, + pathsPresentInSnapshotChanges, + pathsAbsentFromSnapshotChanges + }; + } + const content = { + schema: NATIVE_IMPLEMENTATION_SCOPE_SCHEMA, + contractHash, + baselineProjectionRef, + baselineProjectionHash, + currentProjectionRef, + currentProjectionHash: currentProjectionHash2, + complete: root.complete, + declaredArtifacts, + changes, + unattributed, + unresolvedScopes, + noCodeReason, + ...gitAdvisory ? { gitAdvisory } : {} + }; + const scopeHash = scopeHashValue(root.scopeHash, "Implementation scope scopeHash"); + if (canonicalHash(IMPLEMENTATION_SCOPE_HASH_TAG, content) !== scopeHash) { + throw new Error("Implementation scope content hash mismatch"); + } + return { ...content, scopeHash }; +} +function parseScopeAuthority(value) { + const root = scopeRecord(value, "Native implementation scope authority"); + exactScopeKeys( + root, + ["contractHash", "declaredArtifacts", "noCodeReason"], + ["gitChangedPaths"], + "Native implementation scope authority" + ); + if (!Array.isArray(root.declaredArtifacts)) { + throw new Error("Native implementation scope authority declarations must be an array"); + } + const declaredArtifacts = root.declaredArtifacts.map(parseDeclaredArtifact); + if (JSON.stringify(declaredArtifacts) !== JSON.stringify(normalizeDeclaredArtifacts(declaredArtifacts))) { + throw new Error("Native implementation scope authority declarations must be canonical"); + } + if (root.noCodeReason !== null && (typeof root.noCodeReason !== "string" || root.noCodeReason.length === 0 || root.noCodeReason.trim() !== root.noCodeReason)) { + throw new Error("Native implementation scope authority no-code reason is invalid"); + } + let gitChangedPaths; + if (root.gitChangedPaths !== void 0) { + gitChangedPaths = parseSortedPaths( + root.gitChangedPaths, + "Native implementation scope authority Git paths" + ); + } + const authority = normalizeScopeAuthority({ + contractHash: root.contractHash, + declaredArtifacts, + noCodeReason: root.noCodeReason, + ...gitChangedPaths === void 0 ? {} : { gitChangedPaths } + }); + if (authority.contractHash !== root.contractHash) { + throw new Error("Native implementation scope authority contract hash is not canonical"); + } + return authority; +} +function parseNativeImplementationScopeBundle(value) { + const root = scopeRecord(value, "Native implementation scope bundle"); + exactScopeKeys( + root, + ["authority", "baseline", "current", "scope"], + [], + "Native implementation scope bundle" + ); + const authority = parseScopeAuthority(root.authority); + const baseline = parseNativeSnapshotProjection(root.baseline); + const current = parseNativeSnapshotProjection(root.current); + const suppliedScope = parseNativeImplementationScope(root.scope); + const rebuiltScope = buildScopeFromProjections(baseline, current, authority); + if (JSON.stringify(suppliedScope) !== JSON.stringify(rebuiltScope)) { + throw new Error("Native implementation scope does not match its authoritative bundle"); + } + return { authority, baseline, current, scope: rebuiltScope }; +} +function rebuildNativeImplementationScopeBundle(input) { + const suppliedScope = parseNativeImplementationScope(input.scope); + return parseNativeImplementationScopeBundle({ + authority: { + contractHash: suppliedScope.contractHash, + declaredArtifacts: suppliedScope.declaredArtifacts, + noCodeReason: suppliedScope.noCodeReason, + ...suppliedScope.gitAdvisory ? { gitChangedPaths: suppliedScope.gitAdvisory.changedPaths } : {} + }, + baseline: input.baseline, + current: input.current, + scope: suppliedScope + }); +} + +// domains/comet-native/native-verification-evidence.ts +import path21 from "node:path"; + +// domains/comet-native/native-redaction.ts +var AUTHORIZATION_PATTERN = /\b(Bearer|Basic)\s+[^\s"']+/giu; +var PRIVATE_KEY_PATTERN = /-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/gu; +var URI_CREDENTIAL_PATTERN = /(https?:\/\/)[^\s/@:]+:[^\s/@]+@/giu; +var KNOWN_TOKEN_PATTERN = /\b(?:sk-(?:proj-)?[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{16,}|AKIA[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{20,})\b/gu; +var JSON_CREDENTIAL_PATTERN = /("(?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie)"\s*:\s*)"(?:\\[\s\S]|[^"\\\r\n])*"/giu; +var QUOTED_CREDENTIAL_PATTERN = /(["'])((?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie))\1(\s*:\s*)(["'])[^\r\n]*?\4/giu; +var CREDENTIAL_ASSIGNMENT_PATTERN = /\b((?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie))\b(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;"']+)/giu; +function redactNativeCredentialText(value) { + return value.replace(AUTHORIZATION_PATTERN, "$1 [REDACTED]").replace(PRIVATE_KEY_PATTERN, "[REDACTED PRIVATE KEY]").replace(URI_CREDENTIAL_PATTERN, "$1[REDACTED]@").replace(KNOWN_TOKEN_PATTERN, "[REDACTED TOKEN]").replace(JSON_CREDENTIAL_PATTERN, '$1"[REDACTED]"').replace(QUOTED_CREDENTIAL_PATTERN, "$1$2$1$3$4[REDACTED]$4").replace(CREDENTIAL_ASSIGNMENT_PATTERN, "$1$2[REDACTED]"); +} + +// domains/comet-native/native-verification-evidence.ts +var HASH_PATTERN6 = /^[a-f0-9]{64}$/u; +var MISSING_ACCEPTANCE_DETAIL_LIMIT = 8; +var ACCEPTANCE_TRACE_HASH_TAG = "comet.native.acceptance-trace.v1"; +var PARTIAL_ALLOWANCE_HASH_TAG = "comet.native.partial-allowance.v1"; +var VERIFICATION_ENVELOPE_HASH_TAG = "comet.native.verification-evidence.v1"; +function hash2(value, label) { + if (!HASH_PATTERN6.test(value)) throw new Error(`${label} must be a SHA-256 hash`); + return value; +} +function positiveRevision2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native evidence source revision must be a positive integer"); + } + return value; +} +function changeName(value) { + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(value)) { + throw new Error(`Invalid Native evidence change name: ${value}`); + } + return value; +} +function requiredText(value, label, max = 2e3) { + const normalized = redactNativeCredentialText(value).trim(); + if (normalized.length === 0 || normalized.length > max) { + throw new Error(`${label} must be between 1 and ${max} characters`); + } + return normalized; +} +function portableRef(value, label) { + const normalized = path21.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path21.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized relative ref`); + } + return value; +} +function portableEvidenceRef(value, label, nativeRootRef3) { + const reference = portableRef(value, label); + const sensitiveReason = nativeSensitiveRelativePathReason(reference); + const lowerReference = reference.toLowerCase(); + const lowerNativeRoot = nativeRootRef3 ? portableRef(nativeRootRef3, "Native root ref").toLowerCase() : null; + if (sensitiveReason || lowerReference === "runtime" || lowerReference.startsWith("runtime/") || lowerNativeRoot !== null && (lowerReference === lowerNativeRoot || lowerReference.startsWith(`${lowerNativeRoot}/`))) { + throw new Error( + `${label} is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${reference}` + ); + } + return reference; +} +function checkReceiptRef(value) { + const reference = portableRef(value, "Verification receipt ref"); + if (!/^runtime\/evidence\/check-receipts\/[a-f0-9]{64}\.json$/u.test(reference)) { + throw new Error("Verification receipt ref must identify a Native check receipt"); + } + return reference; +} +function timestamp(value) { + const result2 = value.toISOString(); + if (Number.isNaN(Date.parse(result2))) throw new Error("Native evidence timestamp is invalid"); + return result2; +} +function acceptanceCriteriaHash(criteria) { + return canonicalHash( + "comet.native.acceptance-set.v1", + [...criteria].sort((left, right) => compareText4(left.id, right.id)) + ); +} +function compareText4(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function buildNativeAcceptanceEvidenceTrace(criteria, evidence, options) { + const nativeRootRef3 = portableRef(options.nativeRootRef, "Native root ref"); + const byId = new Map(criteria.map((criterion2) => [criterion2.id, criterion2])); + if (byId.size !== criteria.length) + throw new Error("Native contract has duplicate acceptance IDs"); + const evidenceById = /* @__PURE__ */ new Map(); + for (const entry2 of evidence) { + if (!byId.has(entry2.acceptance_id)) { + throw new Error(`Verification references unknown acceptance ID: ${entry2.acceptance_id}`); + } + if (evidenceById.has(entry2.acceptance_id)) { + throw new Error(`Verification repeats acceptance ID: ${entry2.acceptance_id}`); + } + evidenceById.set(entry2.acceptance_id, entry2); + } + const missing = [...byId.keys()].filter((id) => !evidenceById.has(id)); + if (missing.length > 0) { + const shown = missing.slice(0, MISSING_ACCEPTANCE_DETAIL_LIMIT); + const remainder = missing.length - shown.length; + throw new Error( + `Verification is missing ${missing.length} acceptance evidence entr${missing.length === 1 ? "y" : "ies"}: ${shown.join(", ")}${remainder > 0 ? `, ... (${remainder} more)` : ""}` + ); + } + const entries = [...byId.values()].sort((left, right) => compareText4(left.id, right.id)).map((criterion2) => { + const entry2 = evidenceById.get(criterion2.id); + const evidenceRefs = [...entry2.evidence_refs].map( + (reference) => portableEvidenceRef(reference, `Evidence ref for ${criterion2.id}`, nativeRootRef3) + ).sort(); + if (new Set(evidenceRefs).size !== evidenceRefs.length) { + throw new Error(`Verification repeats an evidence ref for ${criterion2.id}`); + } + const rawSkippedReason = entry2.skipped_reason?.trim() || null; + const skippedReason = rawSkippedReason === null ? null : requiredText(rawSkippedReason, `Skipped reason for ${criterion2.id}`); + if (evidenceRefs.length === 0 === (skippedReason === null)) { + throw new Error( + `Acceptance ${criterion2.id} requires exactly one of evidence refs or skipped reason` + ); + } + return { + acceptanceId: criterion2.id, + kind: criterion2.kind, + source: portableRef(criterion2.source, `Acceptance source for ${criterion2.id}`), + evidenceRefs, + skippedReason + }; + }); + const criteriaHash = acceptanceCriteriaHash(criteria); + const content = { + schema: "comet.native.acceptance-trace.v1", + nativeRootRef: nativeRootRef3, + criteriaHash, + total: entries.length, + evidenced: entries.filter((entry2) => entry2.evidenceRefs.length > 0).length, + skipped: entries.filter((entry2) => entry2.skippedReason !== null).length, + entries + }; + return { + ...content, + traceHash: canonicalHash(ACCEPTANCE_TRACE_HASH_TAG, content) + }; +} +function buildNativePartialAllowance(input) { + const scope = parseNativeImplementationScopeBundle(input.scopeBundle).scope; + if (scope.complete) throw new Error("Complete implementation scope cannot be partially allowed"); + const unresolved = new Set(scope.unresolvedScopes.map((entry2) => entry2.id)); + if (new Set(input.allowedScopeIds).size !== input.allowedScopeIds.length) { + throw new Error("Partial allowance has duplicate allowed scope IDs"); + } + const scopeIds = [...input.allowedScopeIds].sort(compareText4); + if (scopeIds.length === 0) throw new Error("Partial allowance requires at least one scope ID"); + if ([...unresolved, ...scopeIds].some((id) => !/^scope:[a-f0-9]{64}$/u.test(id))) { + throw new Error("Partial allowance scope IDs are invalid"); + } + const unknown = scopeIds.filter((id) => !unresolved.has(id)); + const missing = [...unresolved].filter((id) => !scopeIds.includes(id)); + if (unknown.length > 0) throw new Error(`Partial allowance has unknown scope IDs: ${unknown}`); + if (missing.length > 0) throw new Error(`Partial allowance is missing scope IDs: ${missing}`); + const content = { + schema: "comet.native.partial-allowance.v1", + change: changeName(input.change), + scopeHash: scope.scopeHash, + scopeIds, + reason: requiredText(input.reason, "Partial allowance reason"), + confirmedSummary: requiredText(input.confirmedSummary, "Partial allowance confirmation"), + sourceRevision: positiveRevision2(input.sourceRevision), + confirmedAt: timestamp(input.now ?? /* @__PURE__ */ new Date()) + }; + return { + ...content, + allowanceHash: canonicalHash(PARTIAL_ALLOWANCE_HASH_TAG, content) + }; +} +function buildNativeVerificationEvidenceEnvelope(input) { + if (input.result !== "pass" && input.result !== "fail") { + throw new Error("Native verification evidence result is invalid"); + } + const acceptanceTrace = parseNativeAcceptanceEvidenceTrace(input.acceptanceTrace); + const implementationScope = parseNativeImplementationScopeBundle( + input.implementationScope.bundle + ).scope; + const implementationScopeRef = evidenceDocumentRef( + input.implementationScope.ref, + "scopes", + implementationScope.scopeHash + ); + if (implementationScope.contractHash !== input.contractHash) { + throw new Error("Implementation scope does not match the verification contract"); + } + const allowanceInput = input.partialAllowance ?? null; + const parsedAllowance = allowanceInput ? parseNativePartialAllowance(allowanceInput.allowance) : null; + const allowance = allowanceInput && parsedAllowance ? { + ref: evidenceDocumentRef(allowanceInput.ref, "allowances", parsedAllowance.allowanceHash), + allowance: parsedAllowance + } : null; + if (implementationScope.complete && allowance !== null) { + throw new Error("Complete implementation scope must not use a partial allowance"); + } + if (!implementationScope.complete && allowance === null) { + throw new Error("Partial implementation scope requires a confirmed allowance"); + } + const unresolvedScopeIds = implementationScope.unresolvedScopes.map((entry2) => entry2.id).sort(compareText4); + if (allowance && (allowance.allowance.change !== input.change || allowance.allowance.scopeHash !== implementationScope.scopeHash || JSON.stringify(allowance.allowance.scopeIds) !== JSON.stringify(unresolvedScopeIds))) { + throw new Error("Partial allowance does not match the verification scope"); + } + if (acceptanceTrace.criteriaHash !== input.acceptanceHash) { + throw new Error("Acceptance trace does not match the verification contract"); + } + if (allowance && allowance.allowance.sourceRevision >= input.sourceRevision) { + throw new Error("Partial allowance must precede the verification evidence revision"); + } + const content = { + schema: "comet.native.verification-evidence.v1", + change: changeName(input.change), + sourceRevision: positiveRevision2(input.sourceRevision), + result: input.result, + freshness: implementationScope.complete ? "complete" : "partial", + contractHash: hash2(input.contractHash, "Verification contractHash"), + acceptanceCriteriaHash: hash2(input.acceptanceHash, "Verification acceptanceHash"), + implementationScopeRef, + implementationScopeHash: implementationScope.scopeHash, + reportRef: portableEvidenceRef(input.reportRef, "Verification report ref"), + reportHash: hash2(input.reportHash, "Verification report hash"), + acceptanceTrace, + partialAllowanceRef: allowance?.ref ?? null, + partialAllowanceHash: allowance?.allowance.allowanceHash ?? null, + receiptRef: input.receiptRef ? checkReceiptRef(input.receiptRef) : null, + createdAt: timestamp(input.now ?? /* @__PURE__ */ new Date()) + }; + return { + ...content, + envelopeHash: canonicalHash(VERIFICATION_ENVELOPE_HASH_TAG, content) + }; +} +function evidenceRecord(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactEvidenceKeys(value, keys, label) { + const expected = new Set(keys); + const unknown = Object.keys(value).filter((key) => !expected.has(key)); + const missing = keys.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function canonicalTimestamp(value, label) { + if (typeof value !== "string") throw new Error(`${label} is invalid`); + const parsed = new Date(value); + if (Number.isNaN(parsed.valueOf()) || parsed.toISOString() !== value) { + throw new Error(`${label} is invalid`); + } + return value; +} +function evidenceDocumentRef(value, kind, hash6) { + if (typeof value !== "string") throw new Error(`Native ${kind} evidence ref is invalid`); + const expected = `runtime/evidence/${kind}/${hash6}.json`; + if (value !== expected) throw new Error(`Native ${kind} evidence ref/hash mismatch`); + return value; +} +function parseNativeAcceptanceEvidenceTrace(value) { + const root = evidenceRecord(value, "Native acceptance trace"); + exactEvidenceKeys( + root, + [ + "schema", + "nativeRootRef", + "criteriaHash", + "total", + "evidenced", + "skipped", + "entries", + "traceHash" + ], + "Native acceptance trace" + ); + if (root.schema !== "comet.native.acceptance-trace.v1" || typeof root.nativeRootRef !== "string" || typeof root.criteriaHash !== "string" || !HASH_PATTERN6.test(root.criteriaHash) || !Number.isSafeInteger(root.total) || !Number.isSafeInteger(root.evidenced) || !Number.isSafeInteger(root.skipped) || !Array.isArray(root.entries)) { + throw new Error("Native acceptance trace is invalid"); + } + const nativeRootRef3 = portableRef(root.nativeRootRef, "Native root ref"); + const entries = root.entries.map((value2, index) => { + const entry2 = evidenceRecord(value2, `Native acceptance trace entry ${index}`); + exactEvidenceKeys( + entry2, + ["acceptanceId", "kind", "source", "evidenceRefs", "skippedReason"], + `Native acceptance trace entry ${index}` + ); + if (typeof entry2.acceptanceId !== "string" || !/^acceptance-[a-f0-9]{64}$/u.test(entry2.acceptanceId) || entry2.kind !== "brief-example" && entry2.kind !== "spec-scenario" || typeof entry2.source !== "string" || !Array.isArray(entry2.evidenceRefs) || entry2.evidenceRefs.some((reference) => typeof reference !== "string") || entry2.skippedReason !== null && (typeof entry2.skippedReason !== "string" || entry2.skippedReason.length === 0 || entry2.skippedReason.trim() !== entry2.skippedReason)) { + throw new Error(`Native acceptance trace entry ${index} is invalid`); + } + const evidenceRefs = entry2.evidenceRefs.map( + (reference) => portableEvidenceRef( + reference, + `Native acceptance trace entry ${index} evidence ref`, + nativeRootRef3 + ) + ); + if (JSON.stringify(evidenceRefs) !== JSON.stringify([...new Set(evidenceRefs)].sort(compareText4)) || evidenceRefs.length === 0 === (entry2.skippedReason === null)) { + throw new Error(`Native acceptance trace entry ${index} evidence state is invalid`); + } + return { + acceptanceId: entry2.acceptanceId, + kind: entry2.kind, + source: portableRef(entry2.source, `Native acceptance trace entry ${index} source`), + evidenceRefs, + skippedReason: entry2.skippedReason === null ? null : requiredText( + entry2.skippedReason, + `Native acceptance trace entry ${index} skipped reason` + ) + }; + }); + if (JSON.stringify(entries) !== JSON.stringify( + [...entries].sort((left, right) => compareText4(left.acceptanceId, right.acceptanceId)) + ) || new Set(entries.map((entry2) => entry2.acceptanceId)).size !== entries.length || root.total !== entries.length || root.evidenced !== entries.filter((entry2) => entry2.evidenceRefs.length > 0).length || root.skipped !== entries.filter((entry2) => entry2.skippedReason !== null).length) { + throw new Error("Native acceptance trace entries are inconsistent"); + } + const content = { + schema: "comet.native.acceptance-trace.v1", + nativeRootRef: nativeRootRef3, + criteriaHash: root.criteriaHash, + total: root.total, + evidenced: root.evidenced, + skipped: root.skipped, + entries + }; + const traceHash = hash2(root.traceHash, "Native acceptance trace hash"); + if (canonicalHash(ACCEPTANCE_TRACE_HASH_TAG, content) !== traceHash) { + throw new Error("Native acceptance trace content hash mismatch"); + } + return { ...content, traceHash }; +} +function parseNativePartialAllowance(value) { + const root = evidenceRecord(value, "Native partial allowance"); + exactEvidenceKeys( + root, + [ + "schema", + "change", + "scopeHash", + "scopeIds", + "reason", + "confirmedSummary", + "sourceRevision", + "confirmedAt", + "allowanceHash" + ], + "Native partial allowance" + ); + if (root.schema !== "comet.native.partial-allowance.v1" || typeof root.change !== "string" || typeof root.scopeHash !== "string" || !Array.isArray(root.scopeIds) || root.scopeIds.length === 0 || root.scopeIds.some((id) => typeof id !== "string" || !/^scope:[a-f0-9]{64}$/u.test(id)) || JSON.stringify(root.scopeIds) !== JSON.stringify([...new Set(root.scopeIds)].sort(compareText4)) || typeof root.reason !== "string" || typeof root.confirmedSummary !== "string") { + throw new Error("Native partial allowance is invalid"); + } + const content = { + schema: "comet.native.partial-allowance.v1", + change: changeName(root.change), + scopeHash: hash2(root.scopeHash, "Native partial allowance scopeHash"), + scopeIds: root.scopeIds, + reason: requiredText(root.reason, "Partial allowance reason"), + confirmedSummary: requiredText(root.confirmedSummary, "Partial allowance confirmation"), + sourceRevision: positiveRevision2(root.sourceRevision), + confirmedAt: canonicalTimestamp(root.confirmedAt, "Native partial allowance timestamp") + }; + if (content.reason !== root.reason || content.confirmedSummary !== root.confirmedSummary) { + throw new Error("Native partial allowance text is not canonical"); + } + const allowanceHash = hash2(root.allowanceHash, "Native partial allowance hash"); + if (canonicalHash(PARTIAL_ALLOWANCE_HASH_TAG, content) !== allowanceHash) { + throw new Error("Native partial allowance content hash mismatch"); + } + return { ...content, allowanceHash }; +} +function parseNativeVerificationEvidenceEnvelope(value) { + const root = evidenceRecord(value, "Native verification evidence"); + exactEvidenceKeys( + root, + [ + "schema", + "change", + "sourceRevision", + "result", + "freshness", + "contractHash", + "acceptanceCriteriaHash", + "implementationScopeRef", + "implementationScopeHash", + "reportRef", + "reportHash", + "acceptanceTrace", + "partialAllowanceRef", + "partialAllowanceHash", + "receiptRef", + "createdAt", + "envelopeHash" + ], + "Native verification evidence" + ); + if (root.schema !== "comet.native.verification-evidence.v1" || typeof root.change !== "string" || root.result !== "pass" && root.result !== "fail" || root.freshness !== "complete" && root.freshness !== "partial" || typeof root.implementationScopeHash !== "string" || typeof root.reportRef !== "string" || root.receiptRef !== null && typeof root.receiptRef !== "string") { + throw new Error("Native verification evidence is invalid"); + } + const implementationScopeHash = hash2( + root.implementationScopeHash, + "Native verification implementation scope hash" + ); + const acceptanceTrace = parseNativeAcceptanceEvidenceTrace(root.acceptanceTrace); + const acceptanceCriteriaHash2 = hash2( + root.acceptanceCriteriaHash, + "Native verification acceptance criteria hash" + ); + if (acceptanceTrace.criteriaHash !== acceptanceCriteriaHash2) { + throw new Error("Native verification acceptance trace does not match its criteria hash"); + } + const hasAllowance = root.partialAllowanceRef !== null || root.partialAllowanceHash !== null; + if (root.partialAllowanceRef === null !== (root.partialAllowanceHash === null) || root.freshness === "complete" && hasAllowance || root.freshness === "partial" && !hasAllowance) { + throw new Error("Native verification partial allowance state is invalid"); + } + const partialAllowanceHash = root.partialAllowanceHash === null ? null : hash2(root.partialAllowanceHash, "Native verification allowance hash"); + const result2 = root.result; + const freshness = root.freshness; + const content = { + schema: "comet.native.verification-evidence.v1", + change: changeName(root.change), + sourceRevision: positiveRevision2(root.sourceRevision), + result: result2, + freshness, + contractHash: hash2(root.contractHash, "Native verification contract hash"), + acceptanceCriteriaHash: acceptanceCriteriaHash2, + implementationScopeRef: evidenceDocumentRef( + root.implementationScopeRef, + "scopes", + implementationScopeHash + ), + implementationScopeHash, + reportRef: portableEvidenceRef(root.reportRef, "Native verification report ref"), + reportHash: hash2(root.reportHash, "Native verification report hash"), + acceptanceTrace, + partialAllowanceRef: partialAllowanceHash === null ? null : evidenceDocumentRef(root.partialAllowanceRef, "allowances", partialAllowanceHash), + partialAllowanceHash, + receiptRef: root.receiptRef === null ? null : checkReceiptRef(root.receiptRef), + createdAt: canonicalTimestamp(root.createdAt, "Native verification timestamp") + }; + const envelopeHash = hash2(root.envelopeHash, "Native verification envelope hash"); + if (canonicalHash(VERIFICATION_ENVELOPE_HASH_TAG, content) !== envelopeHash) { + throw new Error("Native verification evidence content hash mismatch"); + } + return { ...content, envelopeHash }; +} + +// domains/comet-native/native-evidence-storage.ts +var HASH_PATTERN7 = /^[a-f0-9]{64}$/u; +var MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES = MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES; +var MAX_NATIVE_IMPLEMENTATION_SCOPE_BUNDLE_BYTES = 3 * MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES; +function isInside5(parent, target) { + const relative = path22.relative(parent, target); + return relative === "" || !path22.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path22.sep}`); +} +function sameDirectoryIdentity5(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity6(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryIdentity3(directory) { + const stat = await fs15.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native evidence parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs15.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain4(root, directory) { + const lexicalRoot = path22.resolve(root); + const lexicalDirectory = path22.resolve(directory); + if (!isInside5(lexicalRoot, lexicalDirectory)) { + throw new Error("Native evidence path is outside its change"); + } + const chain = [await captureDirectoryIdentity3(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path22.relative(lexicalRoot, lexicalDirectory).split(path22.sep).filter(Boolean)) { + cursor = path22.join(cursor, segment); + const identity = await captureDirectoryIdentity3(cursor); + if (!isInside5(chain[0].realPath, identity.realPath)) { + throw new Error(`Native evidence parent resolves outside its change: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain5(chain) { + for (const identity of chain) { + const stat = await fs15.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity5(identity, stat) || await fs15.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native evidence parent changed while reading: ${identity.path}`); + } + } +} +async function readBoundedEvidenceJson(file, changeRoot, hooks = {}) { + const chain = await captureDirectoryChain4(changeRoot, path22.dirname(file)); + await hooks.afterParentChainCaptured?.(); + const lexical = await fs15.lstat(file); + if (!lexical.isFile() || lexical.isSymbolicLink()) { + throw new Error("Native evidence document must be a regular file"); + } + const realPath = await fs15.realpath(file); + if (!isInside5(chain[0].realPath, realPath)) { + throw new Error("Native evidence document resolves outside its change"); + } + const handle = await fs15.open(file, "r"); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs15.lstat(file), + fs15.realpath(file) + ]); + await verifyDirectoryChain5(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== realPath || !sameFileIdentity6(opened, lexical) || !sameFileIdentity6(opened, pathAfterOpen)) { + throw new Error("Native evidence document changed while opening"); + } + if (opened.size > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error( + `Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes` + ); + } + await hooks.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(64 * 1024); + while (true) { + const remaining = MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error( + `Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes` + ); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + await hooks.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs15.lstat(file), + fs15.realpath(file) + ]); + await verifyDirectoryChain5(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity6(opened, afterHandle) || !sameFileIdentity6(opened, afterPath)) { + throw new Error("Native evidence document changed while reading"); + } + return JSON.parse(Buffer.concat(chunks, total).toString("utf8")); + } finally { + await handle.close(); + } +} +function nativeEvidenceRef(kind, hash6) { + if (!HASH_PATTERN7.test(hash6)) throw new Error("Native evidence hash is invalid"); + return `runtime/evidence/${kind}/${hash6}.json`; +} +function nativeReportEvidenceRef(hash6) { + return nativeEvidenceRef("reports", hash6); +} +async function writeNativeVerificationReportSnapshot(options) { + const encoded = Buffer.from(options.text, "utf8"); + if (encoded.byteLength > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES || createHash12("sha256").update(encoded).digest("hex") !== options.hash) { + throw new Error("Native verification report snapshot hash or size is invalid"); + } + return writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "reports", + hash: options.hash, + value: { + schema: "comet.native.verification-report.v1", + reportHash: options.hash, + content: options.text + } + }); +} +async function readNativeVerificationReportSnapshot(paths, name, hash6) { + if (!HASH_PATTERN7.test(hash6)) throw new Error("Native report evidence hash is invalid"); + const value = await readEvidenceDocument({ paths, name, kind: "reports", hash: hash6 }); + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native report evidence must be an object"); + } + const report = value; + if (Object.keys(report).sort().join(",") !== "content,reportHash,schema" || report.schema !== "comet.native.verification-report.v1" || report.reportHash !== hash6 || typeof report.content !== "string" || createHash12("sha256").update(Buffer.from(report.content, "utf8")).digest("hex") !== hash6) { + throw new Error("Native report evidence does not match its hash"); + } + return report.content; +} +function parseEvidenceRef(ref, expectedKind) { + const match = /^runtime\/evidence\/(snapshots|scopes|allowances|verifications|reports)\/([a-f0-9]{64})\.json$/u.exec( + ref + ); + if (!match || match[1] !== expectedKind) { + throw new Error(`Native evidence ref is invalid for ${expectedKind}`); + } + return match[2]; +} +function evidenceFile(paths, name, kind, hash6) { + return path22.join(nativeChangeDir(paths, name), ...nativeEvidenceRef(kind, hash6).split("/")); +} +async function readEvidenceDocument(options) { + const file = evidenceFile(options.paths, options.name, options.kind, options.hash); + await resolveContainedNativePath(options.paths.nativeRoot, file); + return readBoundedEvidenceJson(file, nativeChangeDir(options.paths, options.name), options.hooks); +} +async function writeEvidenceDocument(options) { + assertEvidenceDocumentBudget(options.value); + const file = evidenceFile(options.paths, options.name, options.kind, options.hash); + await resolveContainedNativePath(options.paths.nativeRoot, file); + try { + const existing = await readEvidenceDocument(options); + if (JSON.stringify(existing) !== JSON.stringify(options.value)) { + throw new Error(`Native evidence hash collision for ${options.hash}`); + } + return nativeEvidenceRef(options.kind, options.hash); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, options.value, { containedRoot: options.paths.nativeRoot }); + const persisted = await readEvidenceDocument(options); + if (JSON.stringify(persisted) !== JSON.stringify(options.value)) { + throw new Error(`Native evidence changed during commit for ${options.hash}`); + } + return nativeEvidenceRef(options.kind, options.hash); +} +function assertEvidenceDocumentBudget(value) { + if (serializedEvidenceBytes2(value) > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error(`Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes`); + } +} +function serializedEvidenceBytes2(value) { + return Buffer.byteLength(JSON.stringify(value, null, 2) + "\n", "utf8"); +} +function parseSnapshot2(value, expectedHash) { + return parseNativeSnapshotProjection(value, expectedHash); +} +function parseScope(value, expectedHash) { + const scope = parseNativeImplementationScope(value); + if (scope.scopeHash !== expectedHash) { + throw new Error("Native implementation scope ref/hash mismatch"); + } + return scope; +} +function parseAllowance(value, expectedName, expectedHash) { + const allowance = parseNativePartialAllowance(value); + if (allowance.change !== expectedName || allowance.allowanceHash !== expectedHash) { + throw new Error("Native partial allowance ref/hash/change mismatch"); + } + return allowance; +} +function parseEnvelope(value, expectedName, expectedHash) { + const evidence = parseNativeVerificationEvidenceEnvelope(value); + if (evidence.change !== expectedName || evidence.envelopeHash !== expectedHash) { + throw new Error("Native verification evidence ref/hash/change mismatch"); + } + return evidence; +} +async function assertEnvelopeDependencies(paths, name, evidence, requireReportSnapshot = false) { + const scope = await readNativeImplementationScope(paths, name, evidence.implementationScopeRef); + if (requireReportSnapshot) { + await readNativeVerificationReportSnapshot(paths, name, evidence.reportHash); + } + if (scope.scopeHash !== evidence.implementationScopeHash || scope.contractHash !== evidence.contractHash || (scope.complete ? "complete" : "partial") !== evidence.freshness) { + throw new Error("Native verification evidence does not match its implementation scope"); + } + if (evidence.partialAllowanceRef === null) return; + const allowance = await readNativePartialAllowance(paths, name, evidence.partialAllowanceRef); + if (allowance.allowanceHash !== evidence.partialAllowanceHash || allowance.scopeHash !== scope.scopeHash || JSON.stringify(allowance.scopeIds) !== JSON.stringify(scope.unresolvedScopes.map((entry2) => entry2.id).sort()) || allowance.sourceRevision >= evidence.sourceRevision) { + throw new Error("Native verification evidence does not match its partial allowance"); + } +} +async function writeNativeImplementationScope(options) { + const bundle = parseNativeImplementationScopeBundle(options.bundle); + const { baseline, current, scope } = bundle; + assertEvidenceDocumentBudget(baseline); + assertEvidenceDocumentBudget(current); + assertEvidenceDocumentBudget(scope); + await writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "snapshots", + hash: scope.baselineProjectionHash, + value: baseline + }); + await writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "snapshots", + hash: scope.currentProjectionHash, + value: current + }); + return writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "scopes", + hash: scope.scopeHash, + value: scope + }); +} +async function readNativeImplementationScopeBundle(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "scopes"); + const scope = parseScope( + await readEvidenceDocument({ paths, name, kind: "scopes", hash: hash6, hooks }), + hash6 + ); + const baselineHash = parseEvidenceRef(scope.baselineProjectionRef, "snapshots"); + const currentHash = parseEvidenceRef(scope.currentProjectionRef, "snapshots"); + const [baseline, current] = await Promise.all([ + readEvidenceDocument({ paths, name, kind: "snapshots", hash: baselineHash }).then( + (value) => parseSnapshot2(value, baselineHash) + ), + readEvidenceDocument({ paths, name, kind: "snapshots", hash: currentHash }).then( + (value) => parseSnapshot2(value, currentHash) + ) + ]); + return rebuildNativeImplementationScopeBundle({ baseline, current, scope }); +} +async function readNativeImplementationScope(paths, name, ref, hooks) { + return (await readNativeImplementationScopeBundle(paths, name, ref, hooks)).scope; +} +async function writeNativePartialAllowance(options) { + const allowance = parseAllowance( + options.allowance, + options.name, + options.allowance.allowanceHash + ); + const scope = await readNativeImplementationScope( + options.paths, + options.name, + nativeEvidenceRef("scopes", allowance.scopeHash) + ); + const unresolvedScopeIds = scope.unresolvedScopes.map((entry2) => entry2.id).sort(); + if (scope.complete || JSON.stringify(unresolvedScopeIds) !== JSON.stringify(allowance.scopeIds)) { + throw new Error("Native partial allowance does not match a persisted partial scope"); + } + return writeEvidenceDocument({ + ...options, + kind: "allowances", + hash: allowance.allowanceHash, + value: allowance + }); +} +async function readNativePartialAllowance(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "allowances"); + return parseAllowance( + await readEvidenceDocument({ paths, name, kind: "allowances", hash: hash6, hooks }), + name, + hash6 + ); +} +async function writeNativeVerificationEvidence(options) { + const evidence = parseEnvelope(options.evidence, options.name, options.evidence.envelopeHash); + await assertEnvelopeDependencies(options.paths, options.name, evidence, true); + return writeEvidenceDocument({ + ...options, + kind: "verifications", + hash: evidence.envelopeHash, + value: evidence + }); +} +async function readNativeVerificationEvidence(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "verifications"); + const evidence = parseEnvelope( + await readEvidenceDocument({ paths, name, kind: "verifications", hash: hash6, hooks }), + name, + hash6 + ); + await assertEnvelopeDependencies(paths, name, evidence); + return evidence; +} + +// domains/comet-native/native-conflict-inspection.ts +async function visibleChangeEntries(paths) { + try { + const directory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: paths.changesDir, + label: "Native conflict changes directory", + maxEntries: NATIVE_CONFLICT_RADAR_LIMITS.maxChanges + }); + await directory.verify(); + return directory.entries; + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } +} +async function collectConflictInput(paths, name) { + const state = await readNativeChange(paths, name); + const [scope, workspace] = await Promise.all([ + state.implementation_scope ? readNativeImplementationScope(paths, name, state.implementation_scope) : null, + // Workspace identity is advisory. A malformed advisory must not suppress deterministic + // capability/artifact conflict facts. + readNativeWorkspaceIdentity(paths, name).catch(() => null) + ]); + return { + name: state.name, + revision: state.revision, + specs: state.spec_changes.map((spec) => ({ + capability: spec.capability, + operation: spec.operation, + baseHash: spec.base_hash + })), + declaredArtifacts: scope?.declaredArtifacts ?? [], + workspaceIdentityHash: workspace?.nativeRootId ?? null + }; +} +async function collectConflictInputs(paths) { + const entries = await visibleChangeEntries(paths); + const names = entries.filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + return Promise.all(names.map((name) => collectConflictInput(paths, name))); +} +async function inspectNativeChangeConflicts(paths, name) { + const input = await collectConflictInputs(paths); + buildNativeConflictRadar(input); + const target = input.find((change) => change.name === name); + if (!target) throw new Error(`Native conflict target is not visible: ${name}`); + let definiteConflictCount = 0; + let possibleOverlapCount = 0; + for (const other of input) { + if (other.name === name) continue; + const relationship2 = buildNativeConflictRadar([target, other]).relationships[0]; + if (relationship2.classification === "definite-conflict") definiteConflictCount += 1; + if (relationship2.classification === "possible-overlap") possibleOverlapCount += 1; + } + return { + definiteConflictCount, + possibleOverlapCount, + findingCodes: [ + ...definiteConflictCount > 0 ? ["native-change-conflict"] : [], + ...possibleOverlapCount > 0 ? ["native-change-overlap"] : [] + ] + }; +} + +// domains/comet-native/native-transition-journal.ts +import { randomUUID as randomUUID3 } from "crypto"; +import { promises as fs16 } from "fs"; +import path23 from "path"; +import { isDeepStrictEqual } from "util"; + +// domains/comet-native/native-repair-stagnation.ts +var NATIVE_REPAIR_SIGNATURE_SCHEMA = "comet.native.repair-signature.v1"; +var NATIVE_REPAIR_STAGNATION_LIMITS = { + warningAtConsecutiveFailures: 2, + manualStopAtConsecutiveFailures: 3, + maxRepairIterations: 12, + maxHistoryRecords: 64, + maxCategories: 16, + maxFailedCheckIds: 128, + maxOverrideSummaryCharacters: 2e3 +}; +var SIGNATURE_HASH_TAG = "comet.native.repair-signature.v1"; +var OVERRIDE_SUMMARY_HASH_TAG = "comet.native.repair-override-summary.v1"; +var HASH_PATTERN8 = /^[a-f0-9]{64}$/u; +var TOKEN_PATTERN = /^[a-z0-9][a-z0-9._:/-]{0,255}$/u; +function compareText5(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function hash3(value, label) { + if (typeof value !== "string" || !HASH_PATTERN8.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function positiveInteger3(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function normalizedTokens(values, label, max, allowEmpty) { + if (!Array.isArray(values) || values.length > max || !allowEmpty && values.length === 0) { + throw new Error(`${label} exceeds its count boundary`); + } + const tokens = values.map((value) => { + if (typeof value !== "string" || !TOKEN_PATTERN.test(value)) { + throw new Error(`${label} contains an invalid token: ${String(value)}`); + } + return value; + }); + return [...new Set(tokens)].sort(compareText5); +} +function normalizeNativeRepairFailureTokens(options) { + const categories = options.categories && options.categories.length > 0 ? options.categories : ["verification-failed"]; + return { + categories: normalizedTokens( + categories, + "Native repair categories", + NATIVE_REPAIR_STAGNATION_LIMITS.maxCategories, + false + ), + failedCheckIds: normalizedTokens( + options.failedCheckIds ?? [], + "Native repair failed check IDs", + NATIVE_REPAIR_STAGNATION_LIMITS.maxFailedCheckIds, + true + ) + }; +} +function buildNativeRepairSignature(facts) { + const tokens = normalizeNativeRepairFailureTokens(facts); + const content = { + schema: NATIVE_REPAIR_SIGNATURE_SCHEMA, + contractHash: hash3(facts.contractHash, "Native repair contract hash"), + implementationScopeHash: hash3( + facts.implementationScopeHash, + "Native repair implementation scope hash" + ), + artifactSnapshotHash: hash3(facts.artifactSnapshotHash, "Native repair artifact snapshot hash"), + categories: tokens.categories, + failedCheckIds: tokens.failedCheckIds + }; + return { + ...content, + signatureHash: canonicalHash(SIGNATURE_HASH_TAG, content) + }; +} +function normalizeHistory(history) { + if (!Array.isArray(history) || history.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxHistoryRecords) { + throw new Error("Native repair history exceeds its record boundary"); + } + let previousIteration = 0; + let previousRevision = 0; + return history.map((record8, index) => { + if (!record8 || record8.kind !== "failure" && record8.kind !== "override") { + throw new Error(`Native repair history record ${index} is invalid`); + } + const expectedKeys = record8.kind === "failure" ? ["iteration", "kind", "revision", "signatureHash"] : ["iteration", "kind", "revision", "signatureHash", "summaryHash"]; + const keys = Object.keys(record8).sort(compareText5); + if (keys.length !== expectedKeys.length || keys.some((key, keyIndex) => key !== expectedKeys[keyIndex])) { + throw new Error(`Native repair history record ${index} fields are invalid`); + } + const iteration = positiveInteger3(record8.iteration, `Native repair history ${index} iteration`); + const revision = positiveInteger3(record8.revision, `Native repair history ${index} revision`); + const previous = index > 0 ? history[index - 1] : null; + const pairedOverride = record8.kind === "override" && previous?.kind === "failure" && iteration === previousIteration && revision >= previousRevision && record8.signatureHash === previous.signatureHash; + if (!pairedOverride && (iteration <= previousIteration || revision <= previousRevision)) { + throw new Error("Native repair history must be strictly ordered by revision and iteration"); + } + previousIteration = iteration; + previousRevision = revision; + const signatureHash = hash3(record8.signatureHash, `Native repair history ${index} signature`); + if (record8.kind === "failure") return { kind: "failure", iteration, revision, signatureHash }; + return { + kind: "override", + iteration, + revision, + signatureHash, + summaryHash: hash3(record8.summaryHash, `Native repair history ${index} summary`) + }; + }); +} +function overrideSummary(value) { + const summary = value.trim(); + if (summary.length === 0 || summary.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxOverrideSummaryCharacters) { + throw new Error("Native repair override summary is invalid"); + } + return summary; +} +function hashNativeRepairOverrideSummary(summary) { + return canonicalHash(OVERRIDE_SUMMARY_HASH_TAG, { + summary: overrideSummary(summary) + }); +} +function decideNativeRepairStagnation(options) { + const signature = buildNativeRepairSignature(options.facts); + const history = normalizeHistory(options.history); + const failures = history.filter( + (record8) => record8.kind === "failure" + ); + const totalRepairFailures = failures.length + 1; + const remainingIterations = Math.max( + 0, + NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations - totalRepairFailures + ); + let consecutiveFailures = 1; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== signature.signatureHash) break; + consecutiveFailures += 1; + } + if (totalRepairFailures >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + return { + disposition: "hard-stop", + reasonCode: "repair-iteration-limit", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (consecutiveFailures < NATIVE_REPAIR_STAGNATION_LIMITS.warningAtConsecutiveFailures) { + return { + disposition: "continue", + reasonCode: "new-failure-signature", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (consecutiveFailures < NATIVE_REPAIR_STAGNATION_LIMITS.manualStopAtConsecutiveFailures) { + return { + disposition: "warn", + reasonCode: "repeated-failure-warning", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (options.override) { + const expected = hash3( + options.override.expectedSignatureHash, + "Native repair override expected signature" + ); + if (expected !== signature.signatureHash) { + throw new Error("Native repair override does not match the current failure signature"); + } + overrideSummary(options.override.summary); + const alreadyUsed2 = history.some( + (record8) => record8.kind === "override" && record8.signatureHash === signature.signatureHash + ); + return { + disposition: alreadyUsed2 ? "manual-stop" : "continue", + reasonCode: alreadyUsed2 ? "override-already-used" : "override-accepted", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: !alreadyUsed2 + }; + } + const alreadyUsed = history.some( + (record8) => record8.kind === "override" && record8.signatureHash === signature.signatureHash + ); + return { + disposition: "manual-stop", + reasonCode: alreadyUsed ? "override-already-used" : "repeated-failure-stop", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; +} + +// domains/comet-native/native-trajectory-limits.ts +var NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS = 4096; +function assertNativeTrajectoryText(value, label) { + if (typeof value !== "string" || value.trim().length === 0 || value.length > NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS) { + throw new Error( + `${label} must be between 1 and ${NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS} characters` + ); + } +} + +// domains/comet-native/native-repair-runtime.ts +var NATIVE_REPAIR_TRAJECTORY_FIELD = "repairStagnation"; +var NATIVE_REPAIR_TRAJECTORY_LIMITS = { + maxEvents: 4096, + maxDataDepth: 8, + maxDataNodes: 4096, + maxTotalDataNodes: 65536, + maxObjectFields: 64, + maxArrayEntries: 256, + maxKeyCharacters: 128, + maxTextCharacters: NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS, + maxEventDataCharacters: 65536, + maxTotalDataCharacters: 1048576, + maxRunIdCharacters: 256 +}; +var HASH_PATTERN9 = /^[a-f0-9]{64}$/u; +var REPAIR_SCOPE_HASH_TAG = "comet.native.repair-scope.v1"; +var EVENT_TYPES2 = /* @__PURE__ */ new Set([ + "run_started", + "action_proposed", + "action_completed", + "eval_completed", + "checkpoint", + "state_migrated", + "state_transitioned", + "command_check_recorded", + "recovery_reconciled" +]); +var EVENT_KEYS2 = ["data", "runId", "sequence", "timestamp", "type"]; +var PROJECTION_KEYS = ["disposition", "overrideSummaryHash", "signatureHash"]; +function compareText6(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function exactKeys(value, expected, label) { + const actual = Object.keys(value).sort(compareText6); + if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) { + throw new Error(`${label} fields are invalid`); + } +} +function record5(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + const prototype = Object.getPrototypeOf(value); + if (prototype !== Object.prototype && prototype !== null) { + throw new Error(`${label} must be a plain object`); + } + return value; +} +function hash4(value, label) { + if (typeof value !== "string" || !HASH_PATTERN9.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function nativeRepairScopeHash(bundle) { + const scope = parseNativeImplementationScopeBundle(bundle).scope; + return canonicalHash(REPAIR_SCOPE_HASH_TAG, { + schema: REPAIR_SCOPE_HASH_TAG, + contractHash: scope.contractHash, + artifactSnapshotHash: scope.currentProjectionHash + }); +} +function boundedRunId(value) { + if (typeof value !== "string" || value.length === 0 || value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxRunIdCharacters || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + })) { + throw new Error("Native repair trajectory run ID is invalid"); + } + return value; +} +function boundedData(value, depth, budget, label, legacyTransitionText = false) { + budget.nodes += 1; + if (budget.nodes > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxDataNodes) { + throw new Error("Native repair trajectory event data exceeds its node boundary"); + } + if (depth > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxDataDepth) { + throw new Error("Native repair trajectory event data exceeds its depth boundary"); + } + if (value === null || typeof value === "boolean") return; + if (typeof value === "number") { + if (!Number.isFinite(value)) { + throw new Error(`${label} contains a non-finite number`); + } + return; + } + if (typeof value === "string") { + if (value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTextCharacters && (!legacyTransitionText || value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters)) { + throw new Error(`${label} contains oversized text`); + } + budget.characters += value.length; + if (budget.characters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters) { + throw new Error("Native repair trajectory event data exceeds its text boundary"); + } + return; + } + if (typeof value !== "object") { + throw new Error(`${label} contains a non-JSON value`); + } + if (budget.ancestors.has(value)) throw new Error(`${label} contains a cycle`); + budget.ancestors.add(value); + try { + if (Array.isArray(value)) { + if (value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxArrayEntries) { + throw new Error(`${label} contains an oversized array`); + } + value.forEach((entry2, index) => boundedData(entry2, depth + 1, budget, `${label}[${index}]`)); + return; + } + const object = record5(value, label); + const keys = Object.keys(object); + if (keys.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxObjectFields) { + throw new Error(`${label} contains too many fields`); + } + for (const key of keys) { + if (key.length === 0 || key.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxKeyCharacters) { + throw new Error(`${label} contains an invalid field name`); + } + budget.characters += key.length; + if (budget.characters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters) { + throw new Error("Native repair trajectory event data exceeds its text boundary"); + } + boundedData( + object[key], + depth + 1, + budget, + `${label}.${key}`, + depth === 0 && (key === "summary" || key === "noCodeReason") + ); + } + } finally { + budget.ancestors.delete(value); + } +} +function parseTimestamp(value) { + if (typeof value !== "string" || value.length > 64) { + throw new Error("Native repair trajectory timestamp is invalid"); + } + const parsed = new Date(value); + if (!Number.isFinite(parsed.valueOf()) || parsed.toISOString() !== value) { + throw new Error("Native repair trajectory timestamp is invalid"); + } + return value; +} +function parseEvent2(value, index, runId) { + const event = record5(value, `Native repair trajectory event ${index + 1}`); + exactKeys(event, EVENT_KEYS2, `Native repair trajectory event ${index + 1}`); + if (!Number.isSafeInteger(event.sequence) || event.sequence !== index + 1) { + throw new Error("Native repair trajectory sequence is invalid"); + } + parseTimestamp(event.timestamp); + if (typeof event.type !== "string" || !EVENT_TYPES2.has(event.type)) { + throw new Error("Native repair trajectory event type is invalid"); + } + if (boundedRunId(event.runId) !== runId) { + throw new Error("Native repair trajectory run ID changed inside the committed prefix"); + } + const budget = { nodes: 0, characters: 0, ancestors: /* @__PURE__ */ new Set() }; + boundedData(event.data, 0, budget, "Native trajectory event data"); + record5(event.data, "Native repair trajectory event data"); + return { + event, + dataNodes: budget.nodes, + dataCharacters: budget.characters + }; +} +function parseNativeRepairTrajectoryProjection(value) { + const projection = record5(value, "Native repair trajectory projection"); + exactKeys(projection, PROJECTION_KEYS, "Native repair trajectory projection"); + const signatureHash = hash4(projection.signatureHash, "Native repair trajectory signature hash"); + if (projection.disposition !== "continue" && projection.disposition !== "warn" && projection.disposition !== "manual-stop" && projection.disposition !== "hard-stop") { + throw new Error("Native repair trajectory disposition is invalid"); + } + const overrideSummaryHash = projection.overrideSummaryHash === null ? null : hash4(projection.overrideSummaryHash, "Native repair trajectory override summary hash"); + if (overrideSummaryHash !== null && projection.disposition !== "continue") { + throw new Error("Native repair trajectory override must continue exactly once"); + } + return { + signatureHash, + disposition: projection.disposition, + overrideSummaryHash + }; +} +function assertProjectionTransition(data, projection) { + if (projection.overrideSummaryHash === null) { + if (data.previousPhase !== "verify" || data.nextPhase !== "build" || data.verificationResult !== "fail") { + throw new Error( + "Native repair failure projection is only valid on a failed Verify-to-Build transition" + ); + } + return; + } + if (projection.disposition !== "continue" || data.previousPhase !== "build" || data.nextPhase !== "verify" || data.verificationResult !== null) { + throw new Error( + "Native repair override projection is only valid on a Build-to-Verify transition" + ); + } +} +function assertCommittedFailureProjection(projection, history) { + const failures = history.filter((entry2) => entry2.kind === "failure"); + const total = failures.length + 1; + if (total > NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + throw new Error("Native repair trajectory commits a failure beyond the hard-stop boundary"); + } + let consecutive = 1; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== projection.signatureHash) break; + consecutive += 1; + } + const expectedDisposition = total >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations ? "hard-stop" : consecutive < NATIVE_REPAIR_STAGNATION_LIMITS.warningAtConsecutiveFailures ? "continue" : consecutive < NATIVE_REPAIR_STAGNATION_LIMITS.manualStopAtConsecutiveFailures ? "warn" : "manual-stop"; + if (projection.disposition !== expectedDisposition || projection.overrideSummaryHash !== null) { + throw new Error( + `Native repair trajectory failure disposition is invalid: expected ${expectedDisposition}` + ); + } +} +function assertCommittedOverrideProjection(projection, history, latestProjection) { + const failures = history.filter((entry2) => entry2.kind === "failure"); + if (failures.length >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + throw new Error("Native repair trajectory cannot override a hard stop"); + } + if (latestProjection?.disposition !== "manual-stop" || latestProjection.signatureHash !== projection.signatureHash || failures.at(-1)?.signatureHash !== projection.signatureHash) { + throw new Error("Native repair trajectory override does not match the latest manual stop"); + } + if (history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === projection.signatureHash + )) { + throw new Error("Native repair trajectory signature was already overridden"); + } +} +function projectNativeRepairHistory(options) { + if (!Array.isArray(options.trajectory) || options.trajectory.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEvents) { + throw new Error("Native repair trajectory exceeds its event boundary"); + } + const runId = boundedRunId(options.runId); + if (!Number.isSafeInteger(options.committedTrajectoryOffset) || options.committedTrajectoryOffset < 0 || options.committedTrajectoryOffset > options.trajectory.length) { + throw new Error("Native repair committed trajectory offset is invalid"); + } + const history = []; + let latestProjection = null; + let activeScopeHash = null; + let iteration = 0; + let totalDataNodes = 0; + let totalDataCharacters = 0; + for (let index = 0; index < options.committedTrajectoryOffset; index += 1) { + const parsed = parseEvent2(options.trajectory[index], index, runId); + const { event } = parsed; + totalDataNodes += parsed.dataNodes; + totalDataCharacters += parsed.dataCharacters; + if (totalDataNodes > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTotalDataNodes || totalDataCharacters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTotalDataCharacters) { + throw new Error("Native repair committed trajectory exceeds its aggregate data boundary"); + } + const data = event.data; + const eventScopeHash = Object.hasOwn(data, "repairScopeHash") ? hash4(data.repairScopeHash, "Native repair trajectory repair scope hash") : Object.hasOwn(data, "implementationScopeHash") ? hash4(data.implementationScopeHash, "Native repair trajectory implementation scope hash") : null; + if (!Object.hasOwn(data, NATIVE_REPAIR_TRAJECTORY_FIELD)) { + if (event.type === "state_transitioned" && data.previousPhase === "build" && data.nextPhase === "verify" && (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash || eventScopeHash === null && (latestProjection?.disposition === "manual-stop" || latestProjection?.disposition === "hard-stop"))) { + history.length = 0; + latestProjection = null; + activeScopeHash = eventScopeHash; + iteration = 0; + continue; + } + if (event.type === "state_transitioned" && data.previousPhase === "verify" && data.nextPhase === "archive" && data.verificationResult === "pass") { + history.length = 0; + latestProjection = null; + activeScopeHash = null; + iteration = 0; + } + continue; + } + if (event.type !== "state_transitioned") { + throw new Error("Native repair projection must belong to a state_transitioned event"); + } + const projection = parseNativeRepairTrajectoryProjection(data[NATIVE_REPAIR_TRAJECTORY_FIELD]); + assertProjectionTransition(data, projection); + if (projection.overrideSummaryHash === null) { + if (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash) { + history.length = 0; + iteration = 0; + } + if (eventScopeHash !== null) activeScopeHash = eventScopeHash; + assertCommittedFailureProjection(projection, history); + iteration += 1; + history.push({ + kind: "failure", + revision: event.sequence, + iteration, + signatureHash: projection.signatureHash + }); + } else { + if (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash) { + throw new Error("Native repair override cannot cross implementation scope progress"); + } + assertCommittedOverrideProjection(projection, history, latestProjection); + const failure = history.at(-1); + if (!failure || failure.kind !== "failure") { + throw new Error("Native repair trajectory override has no failure history"); + } + history.push({ + kind: "override", + revision: event.sequence, + iteration: failure.iteration, + signatureHash: projection.signatureHash, + summaryHash: projection.overrideSummaryHash + }); + } + latestProjection = projection; + if (history.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxHistoryRecords) { + throw new Error("Native repair trajectory history exceeds its record boundary"); + } + } + return { history, latestProjection }; +} +function rebuildNativeRepairHistory(options) { + return projectNativeRepairHistory(options).history; +} +function inspectLatestNativeRepairProjection(options) { + const projection = projectNativeRepairHistory(options).latestProjection; + return projection ? { ...projection } : null; +} +function acceptLatestNativeRepairOverride(options) { + const projected = projectNativeRepairHistory(options); + const request = record5(options.override, "Native repair override request"); + exactKeys(request, ["expectedSignatureHash", "summary"], "Native repair override request"); + const expectedSignatureHash = hash4( + request.expectedSignatureHash, + "Native repair override expected signature" + ); + if (typeof request.summary !== "string") { + throw new Error("Native repair override summary is invalid"); + } + if (projected.latestProjection?.disposition === "hard-stop") { + throw new Error("Native repair hard stop cannot be overridden"); + } + if (projected.latestProjection?.disposition !== "manual-stop" || projected.latestProjection.signatureHash !== expectedSignatureHash) { + throw new Error("Native repair override does not match the latest manual stop"); + } + if (projected.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === expectedSignatureHash + )) { + throw new Error("Native repair signature was already overridden"); + } + const overrideSummaryHash = hashNativeRepairOverrideSummary(request.summary); + return { + history: projected.history, + eventProjection: { + signatureHash: expectedSignatureHash, + disposition: "continue", + overrideSummaryHash + } + }; +} +function nativeRepairFailureFacts(input) { + const envelope = parseNativeVerificationEvidenceEnvelope(input.envelope); + const bundle = parseNativeImplementationScopeBundle(input.implementationScope); + if (envelope.result !== "fail") { + throw new Error("Native repair stagnation requires a failed verification envelope"); + } + if (envelope.contractHash !== bundle.scope.contractHash || envelope.implementationScopeHash !== bundle.scope.scopeHash) { + throw new Error("Native repair evidence does not match the implementation scope authority"); + } + const tokens = normalizeNativeRepairFailureTokens({ + categories: input.categories, + failedCheckIds: input.failedCheckIds + }); + return { + contractHash: bundle.scope.contractHash, + implementationScopeHash: nativeRepairScopeHash(bundle), + artifactSnapshotHash: bundle.scope.currentProjectionHash, + categories: tokens.categories, + failedCheckIds: tokens.failedCheckIds + }; +} +function projectionForDecision(decision, overrideSummaryHash) { + return { + signatureHash: decision.signature.signatureHash, + disposition: decision.disposition, + overrideSummaryHash + }; +} +function runtimeContext(input) { + return { + facts: nativeRepairFailureFacts(input), + history: rebuildNativeRepairHistory(input) + }; +} +function inspectNativeRepairFailure(input) { + const context = runtimeContext(input); + const decision = decideNativeRepairStagnation(context); + return { + ...context, + decision, + eventProjection: projectionForDecision(decision, null) + }; +} + +// domains/comet-native/native-runtime-package.ts +var NATIVE_RUNTIME_PACKAGE = { + root: "/comet/native-runtime", + packageKind: "runtime", + definition: { + apiVersion: "comet/v1alpha1", + kind: "Skill", + metadata: { + name: "comet-native-runtime", + version: "3", + description: "Comet-owned state runtime for the Native workflow." + }, + goal: { + statement: "Advance a Native change only after its current guard passes.", + inputs: [], + outputs: [], + success: ["The Native change and Run state agree on the next phase."] + }, + orchestration: { + mode: "deterministic", + entry: "shape", + steps: [ + { id: "shape", action: { type: "checkpoint" }, next: "build" }, + { id: "build", action: { type: "checkpoint" }, next: "verify" }, + { id: "verify", action: { type: "checkpoint" }, next: "archive" }, + { id: "archive", action: { type: "checkpoint" } } + ] + }, + skills: [], + agents: [], + tools: [] + }, + guardrails: { + allowedSkills: [], + allowedAgents: [], + allowedTools: [], + // Native uses the evidence-bound repair episode budget below the engine seam. The generic + // counter remains an action ID source, not a user-visible permanent stop for long-lived changes. + maxIterations: Number.MAX_SAFE_INTEGER, + maxRetriesPerAction: 2, + confirmationRequiredFor: [] + }, + evals: [] +}; +var NATIVE_RUNTIME_HASH = sha256Text("comet-native-runtime:v3:semantic-repair-budget"); +var NATIVE_LEGACY_RUNTIME_IDENTITIES = [ + { + skillVersion: "2", + skillHash: sha256Text("comet-native-runtime:v2:max-iterations-32") + }, + { + skillVersion: "1", + skillHash: sha256Text("comet-native-runtime:v1") + } +]; +function isCompatibleNativeRuntimeIdentity(run) { + return run.skillVersion === NATIVE_RUNTIME_PACKAGE.definition.metadata.version && run.skillHash === NATIVE_RUNTIME_HASH || NATIVE_LEGACY_RUNTIME_IDENTITIES.some( + (identity) => identity.skillVersion === run.skillVersion && identity.skillHash === run.skillHash + ); +} +var nativePhaseResolver = { + resolveStep({ pkg, state }) { + return pkg.definition.orchestration.steps?.find((step) => step.id === state.currentStep); + }, + resolveNext({ step, outcome }) { + if (step.id === "verify" && outcome.state?.verification_result === "fail") return "build"; + return step.next ?? null; + } +}; + +// domains/comet-native/native-trajectory.ts +async function appendNativeTrajectoryEvent(options) { + const trajectory = await readNativeTrajectory(options.changeDir, options.run.trajectoryRef); + const event = { + sequence: trajectory.length + 1, + timestamp: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + type: options.type, + runId: options.run.runId, + data: options.data + }; + await appendNativeTrajectory(options.changeDir, options.run.trajectoryRef, event); + return event; +} +async function writeNativeCheckpoint2(options) { + const checkpoint = { + runId: options.run.runId, + stateVersion: options.run.iteration, + trajectoryOffset: options.trajectoryOffset, + contextHash: null, + artifactsHash: sha256Text(options.evidenceHash), + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString() + }; + await writeNativeCheckpoint(options.changeDir, options.run.checkpointRef, checkpoint); + return checkpoint; +} + +// domains/comet-native/native-transition-evidence.ts +function nativeAdvanceEvidenceHash(evidence) { + return sha256Text( + JSON.stringify({ + summary: evidence.summary, + confirmed: evidence.confirmed ?? false, + artifacts: [...evidence.artifacts ?? []].sort(), + noCodeReason: evidence.noCodeReason ?? null, + verificationResult: evidence.verificationResult ?? null, + verificationReport: evidence.verificationReport ?? null, + verificationReceipt: evidence.verificationReceipt ?? null, + allowPartialScopeHash: evidence.allowPartialScopeHash ?? null, + partialReason: evidence.partialReason ?? null, + repairFailureCategories: [...evidence.repairFailureCategories ?? []].sort(), + repairFailedCheckIds: [...evidence.repairFailedCheckIds ?? []].sort(), + repairOverrideSignature: evidence.repairOverrideSignature ?? null, + repairOverrideSummary: evidence.repairOverrideSummary ?? null + }) + ); +} + +// domains/comet-native/native-transition-journal.ts +var COMMON_JOURNAL_KEYS = [ + "schema", + "id", + "change", + "evidenceHash", + "createdAt", + "previousState", + "nextState", + "previousRun", + "nextRun", + "eventData" +]; +var LEGACY_JOURNAL_KEYS = new Set(COMMON_JOURNAL_KEYS); +var V2_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + ...COMMON_JOURNAL_KEYS, + "minimum_runtime_version", + "revision" +]); +var CURRENT_JOURNAL_KEYS = /* @__PURE__ */ new Set([...V2_JOURNAL_KEYS, "operation"]); +var NATIVE_TRANSITION_JOURNAL_MAX_BYTES = 512 * 1024; +var REQUIRED_TRANSITION_EVENT_DATA_KEYS = /* @__PURE__ */ new Set([ + "previousPhase", + "nextPhase", + "evidenceHash", + "summary", + "artifacts", + "noCodeReason", + "verificationResult" +]); +var TRANSITION_EVENT_DATA_KEYS = /* @__PURE__ */ new Set([ + ...REQUIRED_TRANSITION_EVENT_DATA_KEYS, + "implementationScopeHash", + "repairScopeHash", + "repairStagnation" +]); +var NativeTransitionMigrationRequiredError = class extends Error { + constructor(change) { + super(`Native transition for ${change} requires doctor migration before recovery`); + this.change = change; + this.name = "NativeTransitionMigrationRequiredError"; + } + change; + code = "native-transition-migration-required"; +}; +function nativeTransitionJournalFile(paths, name) { + return path23.join(nativeChangeDir(paths, name), "runtime", "transition.json"); +} +function nativeTransitionLockName(name) { + return `transition-${name}`; +} +async function acquireNativeTransitionLock(paths, name, operation) { + const lockName2 = nativeTransitionLockName(name); + return acquireNativeLock(paths, lockName2, operation); +} +async function withNativeTransitionLock(paths, name, operation, work) { + const lock = await acquireNativeTransitionLock(paths, name, operation); + try { + return await work(); + } finally { + await releaseNativeLock(lock); + } +} +function journalRecord(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native transition journal must be an object"); + } + return value; +} +function rejectUnknownJournalFields(journal, known) { + const unknown = Object.keys(journal).find((key) => !known.has(key)); + if (unknown) throw new Error(`Native transition journal contains unknown field: ${unknown}`); +} +function positiveInteger4(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function sameValue(left, right) { + return isDeepStrictEqual(left, right); +} +function parseTransitionEventData(value, evidenceHash) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native transition journal event data is invalid"); + } + const record8 = value; + const keys = Object.keys(record8); + const unknown = keys.find((key) => !TRANSITION_EVENT_DATA_KEYS.has(key)); + const missing = [...REQUIRED_TRANSITION_EVENT_DATA_KEYS].find( + (key) => !Object.hasOwn(record8, key) + ); + const expectedSize = REQUIRED_TRANSITION_EVENT_DATA_KEYS.size + (Object.hasOwn(record8, "implementationScopeHash") ? 1 : 0) + (Object.hasOwn(record8, "repairScopeHash") ? 1 : 0) + (Object.hasOwn(record8, "repairStagnation") ? 1 : 0); + if (unknown || missing || keys.length !== expectedSize) { + throw new Error( + `Native transition journal event data keys are invalid${unknown ? `: ${unknown}` : missing ? `: missing ${missing}` : ""}` + ); + } + if (!["shape", "build", "verify", "archive"].includes(record8.previousPhase) || !["shape", "build", "verify", "archive"].includes(record8.nextPhase)) { + throw new Error("Native transition journal event phases are invalid"); + } + if (record8.evidenceHash !== evidenceHash) { + throw new Error("Native transition journal event evidence hash mismatch"); + } + assertNativeTrajectoryText(record8.summary, "Native transition journal event summary"); + if (redactNativeCredentialText(record8.summary) !== record8.summary) { + throw new Error("Native transition journal event summary contains unredacted credentials"); + } + if (!Array.isArray(record8.artifacts) || record8.artifacts.length > 128 || record8.artifacts.some( + (artifact) => typeof artifact !== "string" || artifact.length === 0 || Buffer.byteLength(artifact, "utf8") > 512 + )) { + throw new Error("Native transition journal event artifacts are invalid"); + } + if (record8.noCodeReason !== null) { + assertNativeTrajectoryText( + record8.noCodeReason, + "Native transition journal event no-code reason" + ); + if (redactNativeCredentialText(record8.noCodeReason) !== record8.noCodeReason) { + throw new Error( + "Native transition journal event no-code reason contains unredacted credentials" + ); + } + } + if (record8.verificationResult !== null && record8.verificationResult !== "pass" && record8.verificationResult !== "fail") { + throw new Error("Native transition journal event verification result is invalid"); + } + const implementationScopeHash = Object.hasOwn(record8, "implementationScopeHash") ? record8.implementationScopeHash : null; + if (implementationScopeHash !== null && (typeof implementationScopeHash !== "string" || !/^[a-f0-9]{64}$/.test(implementationScopeHash) || record8.previousPhase !== "build" && record8.previousPhase !== "verify")) { + throw new Error("Native transition journal implementation scope hash is invalid"); + } + const repairStagnation = Object.hasOwn(record8, "repairStagnation") ? parseNativeRepairTrajectoryProjection(record8.repairStagnation) : null; + if (repairStagnation) { + const failureProjection = repairStagnation.overrideSummaryHash === null; + if (failureProjection && (record8.previousPhase !== "verify" || record8.nextPhase !== "build" || record8.verificationResult !== "fail") || !failureProjection && (record8.previousPhase !== "build" || record8.nextPhase !== "verify" || record8.verificationResult !== null)) { + throw new Error("Native transition journal repair projection does not match its phase"); + } + } + const repairScopeHash = Object.hasOwn(record8, "repairScopeHash") ? record8.repairScopeHash : null; + if (repairScopeHash !== null && (typeof repairScopeHash !== "string" || !/^[a-f0-9]{64}$/.test(repairScopeHash) || record8.previousPhase !== "build" && record8.previousPhase !== "verify" || implementationScopeHash === null)) { + throw new Error("Native transition journal repair scope hash is invalid"); + } + return { + previousPhase: record8.previousPhase, + nextPhase: record8.nextPhase, + evidenceHash, + summary: record8.summary, + artifacts: [...record8.artifacts], + noCodeReason: record8.noCodeReason, + verificationResult: record8.verificationResult, + ...implementationScopeHash ? { implementationScopeHash } : {}, + ...repairScopeHash ? { repairScopeHash } : {}, + ...repairStagnation ? { repairStagnation } : {} + }; +} +function parseLegacyTransitionEventData(value, evidenceHash) { + if (value && typeof value === "object" && !Array.isArray(value)) { + const record8 = value; + const keys = Object.keys(record8); + const legacyRebaseKeys = /* @__PURE__ */ new Set([ + "previousPhase", + "nextPhase", + "evidenceHash", + "summary", + "reason" + ]); + if (keys.length === legacyRebaseKeys.size && keys.every((key) => legacyRebaseKeys.has(key)) && record8.reason === "spec-rebase") { + return parseTransitionEventData( + { + previousPhase: record8.previousPhase, + nextPhase: record8.nextPhase, + evidenceHash: record8.evidenceHash, + summary: record8.summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }, + evidenceHash + ); + } + } + return parseTransitionEventData(value, evidenceHash); +} +function validateJournalEnvelope(journal, expectedName, legacyEventData = false) { + if (journal.change !== expectedName) throw new Error("Native transition journal change mismatch"); + if (typeof journal.id !== "string" || journal.id.length === 0) { + throw new Error("Native transition journal id is invalid"); + } + if (typeof journal.evidenceHash !== "string" || !/^[a-f0-9]{64}$/u.test(journal.evidenceHash)) { + throw new Error("Native transition journal evidence hash is invalid"); + } + if (typeof journal.createdAt !== "string" || Number.isNaN(Date.parse(journal.createdAt))) { + throw new Error("Native transition journal timestamp is invalid"); + } + const eventData = legacyEventData ? parseLegacyTransitionEventData(journal.eventData, journal.evidenceHash) : parseTransitionEventData(journal.eventData, journal.evidenceHash); + const nextRun = parseNativeStoredRunStateValue(journal.nextRun); + const previousRun = journal.previousRun === null ? null : parseNativeStoredRunStateValue(journal.previousRun); + return { + id: journal.id, + evidenceHash: journal.evidenceHash, + createdAt: journal.createdAt, + previousRun, + nextRun, + eventData + }; +} +function assertNativeRunMetadata(run, label, allowCompatibleLegacyIdentity = false) { + if (run.skill !== NATIVE_RUNTIME_PACKAGE.definition.metadata.name || (allowCompatibleLegacyIdentity ? !isCompatibleNativeRuntimeIdentity(run) : run.skillVersion !== NATIVE_RUNTIME_PACKAGE.definition.metadata.version || run.skillHash !== NATIVE_RUNTIME_HASH) || run.orchestration !== NATIVE_RUNTIME_PACKAGE.definition.orchestration.mode || run.pendingRef !== NATIVE_RUN_STORAGE.pendingRef || run.trajectoryRef !== NATIVE_RUN_STORAGE.trajectoryRef || run.contextRef !== NATIVE_RUN_STORAGE.contextRef || run.artifactsRef !== NATIVE_RUN_STORAGE.artifactsRef || run.checkpointRef !== NATIVE_RUN_STORAGE.checkpointRef) { + throw new Error(`Native transition journal ${label} metadata or storage refs are invalid`); + } +} +function runInvariantProjection(run) { + return { + runId: run.runId, + skill: run.skill, + skillVersion: run.skillVersion, + skillHash: run.skillHash, + orchestration: run.orchestration, + pendingRef: run.pendingRef, + trajectoryRef: run.trajectoryRef, + contextRef: run.contextRef, + artifactsRef: run.artifactsRef, + checkpointRef: run.checkpointRef, + retries: run.retries + }; +} +function assertCommittableRun(run, label) { + if (run.status !== "running") { + throw new Error(`Native transition journal ${label} status must be running`); + } + if (run.pending !== null) { + throw new Error(`Native transition journal ${label} pending action must be null`); + } +} +function validateTransitionRunSemantics(previousState, nextState, envelope, allowCompatibleLegacyIdentity = false) { + const { previousRun, nextRun } = envelope; + assertNativeRunMetadata( + nextRun, + "next Run", + allowCompatibleLegacyIdentity || previousRun !== null + ); + assertCommittableRun(nextRun, "next Run"); + if (nextRun.runId !== nextState.run_id || nextRun.currentStep !== nextState.phase) { + throw new Error("Native transition journal next Run does not match the next change state"); + } + if (previousRun === null) { + if (previousState.run_id !== null || previousState.phase !== "shape" || nextRun.iteration !== 1 || Object.keys(nextRun.retries).length !== 0) { + throw new Error("Native transition journal first Run transition is invalid"); + } + return; + } + assertNativeRunMetadata(previousRun, "previous Run", true); + assertCommittableRun(previousRun, "previous Run"); + if (previousState.run_id !== previousRun.runId || previousRun.currentStep !== previousState.phase || nextState.run_id !== previousRun.runId) { + throw new Error("Native transition journal previous Run does not match the change state"); + } + if (nextRun.iteration !== previousRun.iteration + 1) { + throw new Error("Native transition journal next Run iteration must advance exactly once"); + } + if (!isDeepStrictEqual(runInvariantProjection(previousRun), runInvariantProjection(nextRun))) { + throw new Error("Native transition journal Run identity or storage invariants changed"); + } +} +function specRebaseEvidenceHash(name, summary, specChanges) { + return sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: name, + summary, + specChanges + }) + ); +} +function inferredLegacyTransitionOperation(previousState, nextState, event) { + return previousState.phase !== "shape" && nextState.phase === "build" && event.verificationResult === null ? "spec-rebase" : "advance"; +} +function currentStateRefs(state) { + return "implementation_scope" in state ? { + implementation_scope: state.implementation_scope, + verification_evidence: state.verification_evidence, + partial_allowance: state.partial_allowance + } : null; +} +function validateTransitionStateSemantics(previousState, nextState, envelope, operation, legacyOperation = false) { + const event = envelope.eventData; + const previousRefs = currentStateRefs(previousState); + const nextRefs = currentStateRefs(nextState); + if (event.previousPhase !== previousState.phase || event.nextPhase !== nextState.phase) { + throw new Error("Native transition journal event phases do not match its states"); + } + if (previousState.archived || nextState.archived) { + throw new Error("Native transition journal cannot mutate an archived change"); + } + if (operation === "evidence-retreat") { + const sameIdentity2 = previousState.name === nextState.name && previousState.language === nextState.language && previousState.brief === nextState.brief && previousState.approval === nextState.approval && ("approved_contract_hash" in previousState ? "approved_contract_hash" in nextState && previousState.approved_contract_hash === nextState.approved_contract_hash : !("approved_contract_hash" in nextState)) && previousState.created_at === nextState.created_at && previousState.run_id === nextState.run_id && isDeepStrictEqual(previousState.spec_changes, nextState.spec_changes); + const expectedHash = nativeAdvanceEvidenceHash({ summary: event.summary }); + const validSourcePhase = previousState.phase === "archive" && previousState.verification_result === "pass" || previousState.phase === "verify" && previousState.verification_result === "pending"; + if (!validSourcePhase || nextState.phase !== "build" || nextState.verification_result !== "pending" || nextState.verification_report !== null || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || envelope.evidenceHash !== expectedHash || !sameIdentity2 || previousRefs === null || nextRefs === null || nextRefs.implementation_scope !== null || nextRefs.verification_evidence !== null || nextRefs.partial_allowance !== null) { + throw new Error("Native transition journal evidence retreat semantics are invalid"); + } + return; + } + if (operation === "spec-rebase") { + const currentHash = specRebaseEvidenceHash( + previousState.name, + event.summary, + nextState.spec_changes + ); + const legacyHash = sha256Text(`spec-rebase:${previousState.name}:${event.summary}`); + if (previousState.phase === "shape" || nextState.phase !== "build" || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || nextState.verification_result !== "pending" || nextState.verification_report !== null || !legacyOperation && envelope.evidenceHash !== currentHash || legacyOperation && envelope.evidenceHash !== currentHash && envelope.evidenceHash !== legacyHash || "implementation_scope" in nextState && (nextState.implementation_scope !== null || nextState.verification_evidence !== null || nextState.partial_allowance !== null)) { + throw new Error("Native transition journal spec rebase semantics are invalid"); + } + return; + } + if (previousState.phase === "shape") { + if (nextState.phase !== "build" || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || previousState.verification_result !== "pending" || nextState.verification_result !== "pending" || previousState.verification_report !== null || nextState.verification_report !== null || previousRefs !== null && nextRefs !== null && (previousRefs.implementation_scope !== null || previousRefs.verification_evidence !== null || previousRefs.partial_allowance !== null || nextRefs.implementation_scope !== null || nextRefs.verification_evidence !== null || nextRefs.partial_allowance !== null)) { + throw new Error("Native transition journal Shape to Build semantics are invalid"); + } + return; + } + if (previousState.phase === "build") { + if (nextState.phase !== "verify" || event.verificationResult !== null || event.artifacts.length === 0 && event.noCodeReason === null || nextState.verification_result !== "pending" || nextState.verification_report !== null || previousRefs !== null && nextRefs !== null && (nextRefs.implementation_scope === null || nextRefs.verification_evidence !== null || nextRefs.implementation_scope !== previousRefs.implementation_scope && nextRefs.partial_allowance !== null && nextRefs.partial_allowance === previousRefs.partial_allowance)) { + throw new Error("Native transition journal Build to Verify semantics are invalid"); + } + return; + } + if (previousState.phase === "verify") { + const expectedNext = event.verificationResult === "pass" ? "archive" : "build"; + if (event.verificationResult !== "pass" && event.verificationResult !== "fail" || nextState.phase !== expectedNext || event.artifacts.length !== 0 || event.noCodeReason !== null || nextState.verification_result !== event.verificationResult || nextState.verification_report === null || previousRefs !== null && nextRefs !== null && (previousRefs.implementation_scope === null || nextRefs.implementation_scope !== previousRefs.implementation_scope || nextRefs.partial_allowance !== previousRefs.partial_allowance || previousRefs.verification_evidence !== null || nextRefs.verification_evidence === null)) { + throw new Error("Native transition journal Verify outcome semantics are invalid"); + } + return; + } + throw new Error("Native transition journal cannot advance from Archive"); +} +function parseNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, CURRENT_JOURNAL_KEYS); + if (journal.schema !== NATIVE_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_TRANSITION_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger4( + journal.minimum_runtime_version, + "Native transition minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native transition requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native transition ${NATIVE_TRANSITION_SCHEMA} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger4(journal.revision, "Native transition revision"); + if (revision !== 1) throw new Error("Native transition journal revision must be 1"); + if (journal.operation !== "advance" && journal.operation !== "spec-rebase" && journal.operation !== "evidence-retreat") { + throw new Error("Native transition journal operation is invalid"); + } + const envelope = validateJournalEnvelope(journal, expectedName); + const previousState = parseNativeChangeValue(journal.previousState); + const nextState = parseNativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native transition journal state mismatch"); + } + validateTransitionStateSemantics(previousState, nextState, envelope, journal.operation); + validateTransitionRunSemantics(previousState, nextState, envelope); + if (nextState.revision !== previousState.revision + 1) { + throw new Error("Native transition journal state revision must advance exactly once"); + } + return { + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + operation: journal.operation, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function parseLegacyNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, LEGACY_JOURNAL_KEYS); + if (journal.schema !== NATIVE_LEGACY_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_LEGACY_TRANSITION_SCHEMA}`); + } + const envelope = validateJournalEnvelope(journal, expectedName, true); + const previousState = parseLegacyNativeChangeValue(journal.previousState); + const nextState = parseLegacyNativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native transition journal state mismatch"); + } + validateTransitionStateSemantics( + previousState, + nextState, + envelope, + inferredLegacyTransitionOperation(previousState, nextState, envelope.eventData), + true + ); + validateTransitionRunSemantics(previousState, nextState, envelope, true); + return { + schema: NATIVE_LEGACY_TRANSITION_SCHEMA, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function parseV2NativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, V2_JOURNAL_KEYS); + if (journal.schema !== NATIVE_V2_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_V2_TRANSITION_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger4( + journal.minimum_runtime_version, + "Native v2 transition minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error( + `Native transition ${NATIVE_V2_TRANSITION_SCHEMA} minimum_runtime_version must be 2` + ); + } + const revision = positiveInteger4(journal.revision, "Native v2 transition revision"); + if (revision !== 1) throw new Error("Native v2 transition journal revision must be 1"); + const envelope = validateJournalEnvelope(journal, expectedName, true); + const previousState = parseV2NativeChangeValue(journal.previousState); + const nextState = parseV2NativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native v2 transition journal state mismatch"); + } + validateTransitionStateSemantics( + previousState, + nextState, + envelope, + inferredLegacyTransitionOperation(previousState, nextState, envelope.eventData), + true + ); + validateTransitionRunSemantics(previousState, nextState, envelope, true); + if (nextState.revision !== previousState.revision + 1) { + throw new Error("Native v2 transition journal state revision must advance exactly once"); + } + return { + schema: NATIVE_V2_TRANSITION_SCHEMA, + minimum_runtime_version: 2, + revision, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function inspectNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + if (journal.schema === NATIVE_TRANSITION_SCHEMA) { + return { status: "current", journal: parseNativeTransitionJournalValue(journal, expectedName) }; + } + if (journal.schema === NATIVE_LEGACY_TRANSITION_SCHEMA) { + return { + status: "migration-required", + journal: parseLegacyNativeTransitionJournalValue(journal, expectedName) + }; + } + if (journal.schema === NATIVE_V2_TRANSITION_SCHEMA) { + return { + status: "migration-required", + journal: parseV2NativeTransitionJournalValue(journal, expectedName) + }; + } + throw new Error(`Unsupported Native transition journal schema: ${String(journal.schema)}`); +} +async function inspectPendingNativeTransitionSchema(paths, name) { + const file = nativeTransitionJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_TRANSITION_JOURNAL_MAX_BYTES, + label: `Native transition journal ${name}` + }); + return inspectNativeTransitionJournalValue(JSON.parse(snapshot2.bytes.toString("utf8")), name); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function inspectPendingNativeTransition(paths, name) { + const inspection = await inspectPendingNativeTransitionSchema(paths, name); + if (!inspection) return null; + if (inspection.status === "migration-required") { + throw new NativeTransitionMigrationRequiredError(name); + } + return inspection.journal; +} +async function prepareNativeTransition(options) { + if (await hasPendingNativeSchemaMigration(options.paths, options.nextState.name)) { + throw new Error( + `Native schema migration is incomplete for ${options.nextState.name}; run doctor --repair` + ); + } + await assertNativeTrajectoryHealthy(options.paths, options.nextState.name); + const journal = { + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + operation: options.operation ?? "advance", + id: options.transitionId?.() ?? randomUUID3(), + change: options.nextState.name, + evidenceHash: options.evidenceHash, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + previousState: options.previousState, + nextState: options.nextState, + previousRun: options.previousRun, + nextRun: options.nextRun, + eventData: options.eventData + }; + const validated = parseNativeTransitionJournalValue(journal, journal.change); + const file = nativeTransitionJournalFile(options.paths, validated.change); + await resolveContainedNativePath(options.paths.nativeRoot, file); + if (await inspectPendingNativeTransition(options.paths, validated.change)) { + throw new Error(`Native transition recovery is already pending for ${validated.change}`); + } + await atomicWriteJson(file, validated); + return validated; +} +function assertRunRecoverySource(actual, journal) { + if (sameValue(actual, journal.nextRun)) return "next"; + if (journal.previousRun === null) { + if (actual === null) return "previous"; + } else if (sameValue(actual, journal.previousRun)) { + return "previous"; + } + throw new Error( + `Native transition Run content changed for ${journal.change}; recovery journal was preserved` + ); +} +function assertChangeRecoverySource(actual, journal) { + if (sameValue(actual, journal.nextState)) return "next"; + if (sameValue(actual, journal.previousState)) return "previous"; + throw new Error( + `Native transition change content changed for ${journal.change}; recovery journal was preserved` + ); +} +function expectedTrajectoryEvent(options) { + const { journal } = options; + return trajectoryEventForComparison({ + sequence: options.sequence, + timestamp: journal.createdAt, + type: options.type, + runId: journal.nextRun.runId, + data: options.type === "run_started" ? { + runtime: "comet-native", + phase: journal.previousState.phase, + transitionId: journal.id + } : { ...journal.eventData, transitionId: journal.id } + }); +} +function trajectoryEventForComparison(event) { + return { + sequence: event.sequence, + timestamp: event.timestamp, + type: event.type, + runId: event.runId, + data: event.data + }; +} +function inspectExistingTransitionEvents(trajectory, journal) { + const collisions = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.transitionId === journal.id); + const started = collisions.filter(({ event }) => event.type === "run_started"); + const transitioned = collisions.filter(({ event }) => event.type === "state_transitioned"); + if (collisions.length !== started.length + transitioned.length || started.length > (journal.previousRun === null ? 1 : 0) || transitioned.length > 1 || journal.previousRun === null && transitioned.length === 1 && (started.length !== 1 || started[0].index >= transitioned[0].index)) { + throw new Error( + `Native trajectory transition id collision for ${journal.change}; recovery journal was preserved` + ); + } + for (const item of [...started, ...transitioned]) { + const expected = expectedTrajectoryEvent({ + sequence: item.index + 1, + journal, + type: item.event.type + }); + if (!sameValue(trajectoryEventForComparison(item.event), expected)) { + throw new Error( + `Native trajectory event changed for transition ${journal.id}; recovery journal was preserved` + ); + } + } + return { + started: started[0]?.event ?? null, + transitioned: transitioned[0]?.event ?? null + }; +} +async function continueNativeTransitionLocked(paths, name, hooks) { + if (await hasPendingNativeSchemaMigration(paths, name)) { + throw new Error(`Native schema migration is incomplete for ${name}; run doctor --repair`); + } + await assertNativeTrajectoryHealthy(paths, name); + const journal = await inspectPendingNativeTransition(paths, name); + if (!journal) return null; + const changeDir = nativeChangeDir(paths, name); + const initialEvents = inspectExistingTransitionEvents( + await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef), + journal + ); + const [actualRun, actualChange] = await Promise.all([ + readNativeRunState(changeDir), + readNativeChange(paths, name) + ]); + const runSource = assertRunRecoverySource(actualRun, journal); + const changeSource = assertChangeRecoverySource(actualChange, journal); + if ((initialEvents.started || initialEvents.transitioned) && (runSource !== "next" || changeSource !== "next")) { + throw new Error( + `Native trajectory is ahead of transition state for ${journal.change}; recovery journal was preserved` + ); + } + if (runSource === "previous") { + await writeNativeRunState(changeDir, journal.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + if (!sameValue(await readNativeRunState(changeDir), journal.nextRun)) { + throw new Error( + `Native transition Run content changed while continuing ${journal.change}; recovery journal was preserved` + ); + } + if (changeSource === "previous") { + const persisted = await compareAndSwapNativeChangeLocked( + paths, + journal.nextState, + journal.previousState.revision + ); + if (!sameValue(persisted, journal.nextState)) { + throw new Error( + `Native transition change write diverged for ${journal.change}; recovery journal was preserved` + ); + } + await hooks?.afterChangeStateWritten?.(journal); + } + if (!sameValue(await readNativeChange(paths, name), journal.nextState)) { + throw new Error( + `Native transition change content changed while continuing ${journal.change}; recovery journal was preserved` + ); + } + const activeJournal = await inspectPendingNativeTransition(paths, name); + if (!activeJournal || !sameValue(activeJournal, journal)) { + throw new Error( + `Native transition journal changed while continuing ${journal.change}; it was preserved` + ); + } + const existingEvents = inspectExistingTransitionEvents( + await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef), + journal + ); + if (journal.previousRun === null) { + if (!existingEvents.started) { + await appendNativeTrajectoryEvent({ + changeDir, + run: journal.nextRun, + type: "run_started", + data: { + runtime: "comet-native", + phase: journal.previousState.phase, + transitionId: journal.id + }, + now: new Date(journal.createdAt) + }); + } + } + let event = existingEvents.transitioned; + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir, + run: journal.nextRun, + type: "state_transitioned", + data: { ...journal.eventData, transitionId: journal.id }, + now: new Date(journal.createdAt) + }); + } + await writeNativeCheckpoint2({ + changeDir, + run: journal.nextRun, + trajectoryOffset: event.sequence, + evidenceHash: journal.evidenceHash, + now: new Date(journal.createdAt) + }); + const [finalRun, finalChange] = await Promise.all([ + readNativeRunState(changeDir), + readNativeChange(paths, name) + ]); + assertRunRecoverySource(finalRun, { ...journal, previousRun: journal.nextRun }); + assertChangeRecoverySource(finalChange, { + ...journal, + previousState: journal.nextState + }); + const finalJournal = await inspectPendingNativeTransition(paths, name); + if (!finalJournal || !sameValue(finalJournal, journal)) { + throw new Error( + `Native transition journal changed while continuing ${journal.change}; it was not removed` + ); + } + await fs16.rm(nativeTransitionJournalFile(paths, name), { force: true }); + return journal.nextState; +} +async function continueNativeTransition(paths, name, hooks) { + return withNativeMutationLock( + paths, + `continue transition ${name}`, + () => withNativeTransitionLock( + paths, + name, + `continue transition ${name}`, + () => continueNativeTransitionLocked(paths, name, hooks) + ) + ); +} + +// domains/comet-native/native-verification-runtime.ts +import path28 from "node:path"; + +// domains/comet-native/native-acceptance.ts +import path24 from "node:path"; +var ACCEPTANCE_HASH_TAG = "comet.native.acceptance.v1"; +var ACCEPTANCE_ID_PATTERN = /^acceptance-[a-f0-9]{64}$/u; +var EVIDENCE_ENTRY_KEYS = /* @__PURE__ */ new Set(["acceptance_id", "evidence_refs", "skipped_reason"]); +var ACCEPTANCE_HASH_PATTERN = /^[a-f0-9]{64}$/u; +var ACCEPTANCE_CURSOR_PATTERN = /^native-acceptance-v1\.([a-f0-9]{64})\.([0-9a-z]+)\.([a-f0-9]{64})$/u; +var NATIVE_ACCEPTANCE_PAGE_LIMITS = Object.freeze({ + maxItems: 16, + maxTextBytes: 512, + maxContextItems: 4, + maxContextItemBytes: 256, + maxSerializedBytes: 32 * 1024 +}); +var NATIVE_ACCEPTANCE_LIMITS = Object.freeze({ + maxCriteria: 1024 +}); +var NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER = ""; +var NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER = ""; +function truncateUtf8(value, maxBytes) { + if (Buffer.byteLength(value, "utf8") <= maxBytes) return { value, truncated: false }; + let result2 = ""; + let bytes = 0; + for (const character of value) { + const characterBytes = Buffer.byteLength(character, "utf8"); + if (bytes + characterBytes > maxBytes) break; + result2 += character; + bytes += characterBytes; + } + return { value: result2, truncated: true }; +} +function acceptanceCursor(acceptanceHash, offset) { + const encodedOffset = offset.toString(36); + const cursorHash = canonicalHash("comet.native.acceptance-cursor.v1", { + acceptanceHash, + offset + }); + return `native-acceptance-v1.${acceptanceHash}.${encodedOffset}.${cursorHash}`; +} +function acceptanceOffset(options) { + if (!ACCEPTANCE_HASH_PATTERN.test(options.acceptanceHash)) { + throw new Error("Native acceptance page hash is invalid"); + } + if (options.cursor === void 0 || options.cursor === null) return 0; + const match = ACCEPTANCE_CURSOR_PATTERN.exec(options.cursor); + if (!match) throw new Error("Native acceptance cursor is invalid"); + if (match[1] !== options.acceptanceHash) { + throw new Error("Native acceptance cursor is stale"); + } + const offset = Number.parseInt(match[2], 36); + if (!Number.isSafeInteger(offset) || offset <= 0 || offset >= options.total || offset.toString(36) !== match[2]) { + throw new Error("Native acceptance cursor offset is invalid"); + } + if (match[3] !== canonicalHash("comet.native.acceptance-cursor.v1", { + acceptanceHash: options.acceptanceHash, + offset + })) { + throw new Error("Native acceptance cursor integrity check failed"); + } + return offset; +} +function acceptanceProjection(criterion2) { + const text = truncateUtf8(criterion2.text, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxTextBytes); + const projectedContext = criterion2.context.slice(0, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxContextItems).map((entry2) => truncateUtf8(entry2, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxContextItemBytes)); + return { + id: criterion2.id, + kind: criterion2.kind, + source: criterion2.source, + context: projectedContext.map((entry2) => entry2.value), + text: text.value, + contextTruncated: criterion2.context.length > projectedContext.length || projectedContext.some((entry2) => entry2.truncated), + textTruncated: text.truncated + }; +} +function projectNativeAcceptancePage(options) { + const offset = acceptanceOffset({ + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + cursor: options.cursor + }); + const items = []; + const remaining = options.criteria.slice(offset, offset + NATIVE_ACCEPTANCE_PAGE_LIMITS.maxItems); + for (const criterion2 of remaining) { + const candidate = [...items, acceptanceProjection(criterion2)]; + const nextOffset2 = offset + candidate.length; + const trial = { + schema: "comet.native.acceptance-page.v1", + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + offset, + items: candidate, + nextCursor: nextOffset2 < options.criteria.length ? acceptanceCursor(options.acceptanceHash, nextOffset2) : null, + limits: { ...NATIVE_ACCEPTANCE_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(trial), "utf8") > NATIVE_ACCEPTANCE_PAGE_LIMITS.maxSerializedBytes) { + if (items.length === 0) { + throw new Error("Native acceptance criterion exceeds the page serialization budget"); + } + break; + } + items.push(candidate.at(-1)); + } + if (options.criteria.length > 0 && items.length === 0) { + throw new Error("Native acceptance page could not project its next criterion"); + } + const nextOffset = offset + items.length; + const page = { + schema: "comet.native.acceptance-page.v1", + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + offset, + items, + nextCursor: nextOffset < options.criteria.length ? acceptanceCursor(options.acceptanceHash, nextOffset) : null, + limits: { ...NATIVE_ACCEPTANCE_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(page), "utf8") > NATIVE_ACCEPTANCE_PAGE_LIMITS.maxSerializedBytes) { + throw new Error("Native acceptance page exceeds its serialization budget"); + } + return page; +} +function markdownHeading(line) { + const match = /^ {0,3}(#{1,6})[ \t]+(.+?)[ \t]*$/u.exec(line); + if (!match) return null; + return { + level: match[1].length, + text: match[2].replace(/[ \t]+#+[ \t]*$/u, "").trim() + }; +} +function nextFenceState(line, current) { + const match = /^ {0,3}(`{3,}|~{3,})(.*)$/u.exec(line); + if (!match) return current; + const marker = match[1][0]; + if (current === null) return { marker, length: match[1].length }; + if (marker === current.marker && match[1].length >= current.length && match[2].trim().length === 0) { + return null; + } + return current; +} +function* iterateScannedMarkdown(markdown) { + let fence = null; + let htmlComment = false; + let htmlTag = null; + const normalized = markdown.replace(/\r\n?/gu, "\n"); + let start = 0; + let index = 0; + while (start <= normalized.length) { + const end = normalized.indexOf("\n", start); + const line = end === -1 ? normalized.slice(start) : normalized.slice(start, end); + const body = fence === null && !htmlComment && htmlTag === null; + yield { line, body, index }; + index += 1; + if (fence !== null) { + fence = nextFenceState(line, fence); + } else if (htmlComment) { + if (line.includes("-->")) htmlComment = false; + } else if (htmlTag !== null) { + if (new RegExp(``, "iu").test(line)) htmlTag = null; + } else { + const nextFence = nextFenceState(line, null); + if (nextFence !== null) { + fence = nextFence; + } else { + const trimmed = line.trimStart(); + if (trimmed.startsWith("")) { + htmlComment = true; + } else { + const htmlStart = /^<([A-Za-z][A-Za-z0-9-]*)\b[^>]*>/u.exec(trimmed); + if (htmlStart && !trimmed.startsWith("") && !new RegExp(``, "iu").test(trimmed)) { + htmlTag = htmlStart[1]; + } + } + } + } + if (end === -1) break; + start = end + 1; + } +} +function scanMarkdown(markdown) { + return [...iterateScannedMarkdown(markdown)].map(({ line, body }) => ({ line, body })); +} +function normalizeNativeAcceptanceText(value) { + return value.normalize("NFC").replace(/\s+/gu, " ").trim(); +} +function hasControlCharacter2(value) { + return Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }); +} +function criterion(kind, source, rawText, rawContext = []) { + const text = normalizeNativeAcceptanceText(rawText); + const normalizedSource = source.replaceAll("\\", "/").trim(); + const context = rawContext.map(normalizeNativeAcceptanceText); + if (text.length === 0) throw new Error(`${kind} acceptance criterion must not be empty`); + if (normalizedSource.length === 0) { + throw new Error(`${kind} acceptance criterion source must not be empty`); + } + return { + id: `acceptance-${canonicalHash(ACCEPTANCE_HASH_TAG, { + kind, + source: normalizedSource, + context, + text + })}`, + kind, + source: normalizedSource, + context, + text + }; +} +function uniqueCriteria(criteria, label) { + const seen = /* @__PURE__ */ new Set(); + for (const item of criteria) { + if (seen.has(item.id)) throw new Error(`${label} contains duplicate acceptance criteria`); + seen.add(item.id); + } + return criteria; +} +function acceptanceSectionBounds(markdown) { + let sectionStart = null; + let sectionEnd = null; + let matches = 0; + let lineCount = 0; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + lineCount = index + 1; + const heading = body ? markdownHeading(line) : null; + if (heading?.level !== 1) continue; + if (heading.text.toLocaleLowerCase("en-US") === "acceptance examples") { + matches += 1; + if (matches === 1) sectionStart = index + 1; + } else if (sectionStart !== null && sectionEnd === null) { + sectionEnd = index; + } + } + if (matches === 0 || sectionStart === null) return null; + if (matches !== 1) { + throw new Error("Brief must contain exactly one Acceptance examples section"); + } + return { start: sectionStart, end: sectionEnd ?? lineCount }; +} +function deriveBriefAcceptanceCriteria(markdown, source = "brief.md", maxCriteria = NATIVE_ACCEPTANCE_LIMITS.maxCriteria) { + if (!Number.isSafeInteger(maxCriteria) || maxCriteria < 0) { + throw new Error("Native brief acceptance budget is invalid"); + } + const section = acceptanceSectionBounds(markdown); + if (section === null) return []; + let topLevelIndent = null; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + if (index < section.start || index >= section.end) continue; + const listItem = body ? /^( {0,3})[-*+][ \t]+/u.exec(line) : null; + if (listItem === null) continue; + const indent = listItem[1].length; + topLevelIndent = topLevelIndent === null ? indent : Math.min(topLevelIndent, indent); + } + const items = []; + let active = null; + const pushActive = () => { + if (active === null) return; + if (items.length >= maxCriteria) { + throw new Error(`Native acceptance exceeds its ${maxCriteria}-criterion acceptance budget`); + } + items.push(active); + }; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + if (index < section.start || index >= section.end) continue; + const listItem = body ? /^( {0,3})[-*+][ \t]+(.*)$/u.exec(line) : null; + if (listItem && listItem[1].length === topLevelIndent) { + pushActive(); + active = [listItem[2]]; + } else if (active !== null) { + active.push(line); + } + } + pushActive(); + return uniqueCriteria( + items.map((lines) => criterion("brief-example", source, lines.join("\n"))), + "Brief" + ); +} +function deriveSpecAcceptanceCriteria(markdown, source = "spec.md", maxCriteria = NATIVE_ACCEPTANCE_LIMITS.maxCriteria) { + if (!Number.isSafeInteger(maxCriteria) || maxCriteria < 0) { + throw new Error("Native specification acceptance budget is invalid"); + } + const criteria = []; + const ancestry = []; + let active = null; + const flush = () => { + if (active === null) return; + if (criteria.length >= maxCriteria) { + throw new Error(`Native acceptance exceeds its ${maxCriteria}-criterion acceptance budget`); + } + criteria.push( + criterion("spec-scenario", source, [active.title, ...active.body].join("\n"), active.context) + ); + active = null; + }; + for (const { line, body } of iterateScannedMarkdown(markdown)) { + const heading = body ? markdownHeading(line) : null; + const scenario = heading ? /^Scenario\s*:\s*(.*)$/iu.exec(heading.text) : null; + if (scenario) { + flush(); + while (ancestry.at(-1) && ancestry.at(-1).level >= heading.level) ancestry.pop(); + const title = normalizeNativeAcceptanceText(scenario[1]); + if (title.length === 0) throw new Error("Scenario title must not be empty"); + active = { + level: heading.level, + title, + body: [], + context: ancestry.map((item) => item.text) + }; + } else if (heading) { + if (active !== null && heading.level <= active.level) flush(); + else if (active !== null) active.body.push(line); + while (ancestry.at(-1) && ancestry.at(-1).level >= heading.level) ancestry.pop(); + ancestry.push(heading); + } else if (active !== null && body) { + active.body.push(line); + } + } + flush(); + return uniqueCriteria(criteria, "Specification"); +} +function normalizeEvidenceRef(value, acceptanceId) { + const normalized = value.trim().replaceAll("\\", "/"); + if (normalized.length === 0 || hasControlCharacter2(normalized) || path24.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~|[A-Za-z][A-Za-z0-9+.-]*:)/u.test(normalized) || normalized.split("/").includes("..")) { + throw new Error(`Acceptance evidence ${acceptanceId} has an unsafe evidence ref`); + } + const portable = path24.posix.normalize(normalized); + if (portable === "." || portable === ".." || portable.startsWith("../")) { + throw new Error(`Acceptance evidence ${acceptanceId} has an unsafe evidence ref`); + } + if (portable.split("/").some( + (segment) => segment.toLowerCase() === ".git" || segment.toLowerCase().startsWith(".env") + )) { + throw new Error(`Acceptance evidence ${acceptanceId} references sensitive content`); + } + return portable; +} +function evidenceRecord2(value, index) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Acceptance evidence entry ${index} must be an object`); + } + const record8 = value; + const unknown = Object.keys(record8).filter((key) => !EVIDENCE_ENTRY_KEYS.has(key)); + if (unknown.length > 0) { + throw new Error( + `Acceptance evidence entry ${index} has unknown field(s): ${unknown.join(", ")}` + ); + } + return record8; +} +function validateEvidenceEntries(value) { + if (!Array.isArray(value)) + throw new Error("Native acceptance evidence block must be a JSON array"); + const seenIds = /* @__PURE__ */ new Set(); + return value.map((item, index) => { + const record8 = evidenceRecord2(item, index); + const acceptanceId = record8.acceptance_id; + if (typeof acceptanceId !== "string" || !ACCEPTANCE_ID_PATTERN.test(acceptanceId)) { + throw new Error(`Acceptance evidence entry ${index} has an invalid acceptance_id`); + } + if (seenIds.has(acceptanceId)) { + throw new Error(`Native acceptance evidence has duplicate acceptance_id: ${acceptanceId}`); + } + seenIds.add(acceptanceId); + if (!Array.isArray(record8.evidence_refs)) { + throw new Error(`Acceptance evidence ${acceptanceId} requires an evidence_refs array`); + } + const evidenceRefs = record8.evidence_refs.map((reference) => { + if (typeof reference !== "string" || reference.trim().length === 0) { + throw new Error(`Acceptance evidence ${acceptanceId} has a non-empty string requirement`); + } + return normalizeEvidenceRef(reference, acceptanceId); + }); + if (new Set(evidenceRefs).size !== evidenceRefs.length) { + throw new Error(`Acceptance evidence ${acceptanceId} has a duplicate evidence ref`); + } + let skippedReason; + if (Object.prototype.hasOwnProperty.call(record8, "skipped_reason")) { + if (typeof record8.skipped_reason !== "string" || record8.skipped_reason.trim().length === 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} skipped_reason must be a non-empty string` + ); + } + skippedReason = record8.skipped_reason.trim(); + } + if (evidenceRefs.length === 0 && skippedReason === void 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} requires evidence_refs or skipped_reason` + ); + } + if (evidenceRefs.length > 0 && skippedReason !== void 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} must not include both evidence and a skip` + ); + } + return { + acceptance_id: acceptanceId, + evidence_refs: evidenceRefs, + ...skippedReason === void 0 ? {} : { skipped_reason: skippedReason } + }; + }); +} +function parseNativeVerificationMachineBlock(markdown) { + const lines = scanMarkdown(markdown); + const invalidContextMarker = lines.some( + ({ line, body }) => !body && (line === NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER || line === NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER) + ); + if (invalidContextMarker) { + throw new Error("Native acceptance evidence markers must be in the Markdown body"); + } + const starts = lines.flatMap( + ({ line, body }, index) => body && line === NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER ? [index] : [] + ); + const ends = lines.flatMap( + ({ line, body }, index) => body && line === NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER ? [index] : [] + ); + if (starts.length !== 1 || ends.length !== 1) { + throw new Error("Verification must contain exactly one Native acceptance evidence block"); + } + if (starts[0] >= ends[0]) { + throw new Error("Native acceptance evidence markers are out of order"); + } + const payload = lines.slice(starts[0] + 1, ends[0]).map(({ line }) => line).join("\n").trim(); + if (payload.length === 0) throw new Error("Native acceptance evidence block is empty"); + let parsed; + try { + parsed = JSON.parse(payload); + } catch (error) { + throw new Error( + `Native acceptance evidence block is invalid JSON: ${error.message}`, + { cause: error } + ); + } + const validated = validateEvidenceEntries(parsed); + const canonicalPayload = canonicalEvidencePayload(validated); + if (payload !== canonicalPayload) { + throw new Error("Native acceptance evidence block must use canonical serialization"); + } + return validated; +} +function canonicalEvidencePayload(entries) { + const validated = validateEvidenceEntries([...entries]).map((entry2) => ({ ...entry2, evidence_refs: [...entry2.evidence_refs].sort() })).sort((left, right) => left.acceptance_id.localeCompare(right.acceptance_id)); + return JSON.stringify(validated, null, 2); +} +function serializeNativeVerificationMachineBlock(entries) { + return [ + NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER, + canonicalEvidencePayload(entries), + NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER + ].join("\n"); +} + +// domains/comet-native/native-check-receipt-storage.ts +import { constants as fsConstants4, promises as fs17 } from "node:fs"; +import path26 from "node:path"; + +// domains/comet-native/native-check-receipt-model.ts +import path25 from "node:path"; +var NATIVE_CHECK_RECEIPT_SCHEMA = "comet.native.check-receipt.v1"; +var NATIVE_CHECK_RECEIPT_HASH_TAG = "comet.native.check-receipt.v1"; +var NATIVE_CHECK_POLICY = "scoped-text-safety"; +var NATIVE_CHECK_POLICY_VERSION = 1; +var NATIVE_CHECK_LIMITS = Object.freeze({ + maxFiles: 256, + maxFileBytes: 1024 * 1024, + maxTotalBytes: 8 * 1024 * 1024, + maxIssues: 128 +}); +var HASH_PATTERN10 = /^[a-f0-9]{64}$/u; +var CHANGE_NAME_PATTERN3 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CHECKER_HASH_TAG = "comet.native.checker-policy.v1"; +var CHECK_INPUT_HASH_TAG = "comet.native.check-input.v1"; +var MAX_ISSUE_PATH_BYTES = 2048; +var NATIVE_CHECKER_HASH = canonicalHash(CHECKER_HASH_TAG, { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + limits: NATIVE_CHECK_LIMITS, + checks: ["conflict-marker", "space-before-tab", "trailing-whitespace"], + binaryHandling: "skip-and-count" +}); +var ISSUE_KINDS = /* @__PURE__ */ new Set([ + "conflict-marker", + "trailing-whitespace", + "space-before-tab", + "scope-mismatch", + "unsafe-file", + "scan-limit" +]); +var ISSUE_KIND_ORDER = [ + "conflict-marker", + "trailing-whitespace", + "space-before-tab", + "scope-mismatch", + "unsafe-file", + "scan-limit" +]; +var STALE_REASON_ORDER = [ + "contract-before-does-not-match-scope", + "implementation-before-does-not-match-scope", + "contract-changed-during-check", + "implementation-changed-during-check", + "contract-after-does-not-match-scope", + "implementation-after-does-not-match-scope" +]; +var STALE_REASONS = new Set(STALE_REASON_ORDER); +function record6(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactKeys2(value, expected, label) { + const keys = new Set(expected); + const unknown = Object.keys(value).filter((key) => !keys.has(key)); + const missing = expected.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function hash5(value, label) { + if (typeof value !== "string" || !HASH_PATTERN10.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function nonNegativeInteger2(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function positiveInteger5(value, label) { + const parsed = nonNegativeInteger2(value, label); + if (parsed < 1) throw new Error(`${label} must be a positive integer`); + return parsed; +} +function isoTimestamp2(value, label) { + if (typeof value !== "string") throw new Error(`${label} must be an ISO timestamp`); + const parsed = new Date(value); + if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) { + throw new Error(`${label} must be a canonical ISO timestamp`); + } + return value; +} +function projectRelativePath2(value, label) { + if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > MAX_ISSUE_PATH_BYTES || value.includes("\\") || value.includes("\0") || value.endsWith("/") || /^[a-zA-Z]:/u.test(value)) { + throw new Error(`${label} must be a bounded normalized project-relative path`); + } + const normalized = path25.posix.normalize(value); + if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../") || path25.posix.isAbsolute(normalized)) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function parseChecker(value) { + const checker = record6(value, "Native check receipt checker"); + exactKeys2(checker, ["policy", "version", "hash", "limits"], "Native check receipt checker"); + const limits = record6(checker.limits, "Native check receipt limits"); + exactKeys2( + limits, + ["maxFiles", "maxFileBytes", "maxTotalBytes", "maxIssues"], + "Native check receipt limits" + ); + if (checker.policy !== NATIVE_CHECK_POLICY || checker.version !== NATIVE_CHECK_POLICY_VERSION || checker.hash !== NATIVE_CHECKER_HASH || limits.maxFiles !== NATIVE_CHECK_LIMITS.maxFiles || limits.maxFileBytes !== NATIVE_CHECK_LIMITS.maxFileBytes || limits.maxTotalBytes !== NATIVE_CHECK_LIMITS.maxTotalBytes || limits.maxIssues !== NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt checker policy is unsupported"); + } + return { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + hash: NATIVE_CHECKER_HASH, + limits: { ...NATIVE_CHECK_LIMITS } + }; +} +function parseContract(value) { + const contract = record6(value, "Native check receipt contract"); + exactKeys2(contract, ["expectedHash", "beforeHash", "afterHash"], "Native check receipt contract"); + return { + expectedHash: hash5(contract.expectedHash, "Native check expected contract hash"), + beforeHash: hash5(contract.beforeHash, "Native check before contract hash"), + afterHash: hash5(contract.afterHash, "Native check after contract hash") + }; +} +function parseImplementation(value) { + const implementation = record6(value, "Native check receipt implementation"); + exactKeys2( + implementation, + ["scopeHash", "expectedSnapshotHash", "beforeSnapshotHash", "afterSnapshotHash"], + "Native check receipt implementation" + ); + return { + scopeHash: hash5(implementation.scopeHash, "Native check scope hash"), + expectedSnapshotHash: hash5( + implementation.expectedSnapshotHash, + "Native check expected snapshot hash" + ), + beforeSnapshotHash: hash5( + implementation.beforeSnapshotHash, + "Native check before snapshot hash" + ), + afterSnapshotHash: hash5(implementation.afterSnapshotHash, "Native check after snapshot hash") + }; +} +function parseCounts(value) { + const counts = record6(value, "Native check receipt counts"); + exactKeys2( + counts, + [ + "filesSelected", + "filesScanned", + "binaryFilesSkipped", + "bytesScanned", + "issueCount", + "recordedIssueCount" + ], + "Native check receipt counts" + ); + const parsed = { + filesSelected: nonNegativeInteger2(counts.filesSelected, "Native check filesSelected"), + filesScanned: nonNegativeInteger2(counts.filesScanned, "Native check filesScanned"), + binaryFilesSkipped: nonNegativeInteger2( + counts.binaryFilesSkipped, + "Native check binaryFilesSkipped" + ), + bytesScanned: nonNegativeInteger2(counts.bytesScanned, "Native check bytesScanned"), + issueCount: nonNegativeInteger2(counts.issueCount, "Native check issueCount"), + recordedIssueCount: nonNegativeInteger2( + counts.recordedIssueCount, + "Native check recordedIssueCount" + ) + }; + if (parsed.filesScanned + parsed.binaryFilesSkipped > parsed.filesSelected || parsed.filesScanned + parsed.binaryFilesSkipped > NATIVE_CHECK_LIMITS.maxFiles || parsed.bytesScanned > NATIVE_CHECK_LIMITS.maxTotalBytes || parsed.recordedIssueCount > parsed.issueCount || parsed.recordedIssueCount > NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt count accounting is invalid"); + } + return parsed; +} +function compareIssues(left, right) { + return left.path.localeCompare(right.path, "en") || left.line - right.line || ISSUE_KIND_ORDER.indexOf(left.kind) - ISSUE_KIND_ORDER.indexOf(right.kind); +} +function parseIssues(value) { + if (!Array.isArray(value) || value.length > NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt issues must be a bounded array"); + } + const issues = value.map((entry2, index) => { + const issue = record6(entry2, `Native check issue ${index}`); + exactKeys2(issue, ["path", "line", "kind"], `Native check issue ${index}`); + if (typeof issue.kind !== "string" || !ISSUE_KINDS.has(issue.kind)) { + throw new Error(`Native check issue ${index} kind is invalid`); + } + return { + path: projectRelativePath2(issue.path, `Native check issue ${index} path`), + line: positiveInteger5(issue.line, `Native check issue ${index} line`), + kind: issue.kind + }; + }); + const canonical = [...issues].sort(compareIssues); + if (JSON.stringify(canonical) !== JSON.stringify(issues)) { + throw new Error("Native check receipt issues must be canonical"); + } + return issues; +} +function parseStaleReasons(value) { + if (!Array.isArray(value) || value.some( + (entry2) => typeof entry2 !== "string" || !STALE_REASONS.has(entry2) + )) { + throw new Error("Native check receipt stale reasons are invalid"); + } + const reasons = value; + const canonical = STALE_REASON_ORDER.filter((reason) => reasons.includes(reason)); + if (new Set(reasons).size !== reasons.length || JSON.stringify(canonical) !== JSON.stringify(reasons)) { + throw new Error("Native check receipt stale reasons must be canonical"); + } + return [...reasons]; +} +function nativeCheckInputHash(value) { + return canonicalHash(CHECK_INPUT_HASH_TAG, value); +} +function parseNativeCheckReceipt(value) { + const receipt = record6(value, "Native check receipt"); + exactKeys2( + receipt, + [ + "schema", + "change", + "sourceRevision", + "checker", + "inputHash", + "status", + "startedAt", + "endedAt", + "contract", + "implementation", + "counts", + "issues", + "issuesTruncated", + "stale", + "staleReasons", + "receiptHash" + ], + "Native check receipt" + ); + if (receipt.schema !== NATIVE_CHECK_RECEIPT_SCHEMA) { + throw new Error("Native check receipt schema is invalid"); + } + if (typeof receipt.change !== "string" || Buffer.byteLength(receipt.change, "utf8") > 128 || !CHANGE_NAME_PATTERN3.test(receipt.change)) { + throw new Error("Native check receipt change name is invalid"); + } + const sourceRevision = positiveInteger5(receipt.sourceRevision, "Native check source revision"); + const checker = parseChecker(receipt.checker); + const contract = parseContract(receipt.contract); + const implementation = parseImplementation(receipt.implementation); + const expectedInputHash = nativeCheckInputHash({ + change: receipt.change, + sourceRevision, + checkerHash: checker.hash, + contractHash: contract.expectedHash, + scopeHash: implementation.scopeHash, + snapshotHash: implementation.expectedSnapshotHash + }); + if (hash5(receipt.inputHash, "Native check input hash") !== expectedInputHash) { + throw new Error("Native check receipt input hash mismatch"); + } + if (receipt.status !== "passed" && receipt.status !== "failed") { + throw new Error("Native check receipt status is invalid"); + } + const startedAt = isoTimestamp2(receipt.startedAt, "Native check receipt startedAt"); + const endedAt = isoTimestamp2(receipt.endedAt, "Native check receipt endedAt"); + if (endedAt < startedAt) throw new Error("Native check receipt endedAt precedes startedAt"); + const counts = parseCounts(receipt.counts); + const issues = parseIssues(receipt.issues); + if (counts.recordedIssueCount !== issues.length) { + throw new Error("Native check receipt recorded issue count is inconsistent"); + } + if (typeof receipt.issuesTruncated !== "boolean" || receipt.issuesTruncated !== counts.issueCount > issues.length) { + throw new Error("Native check receipt issue truncation flag is inconsistent"); + } + const staleReasons2 = parseStaleReasons(receipt.staleReasons); + if (typeof receipt.stale !== "boolean" || receipt.stale !== staleReasons2.length > 0) { + throw new Error("Native check receipt stale flag is inconsistent"); + } + const expectedStatus = counts.issueCount === 0 && !receipt.stale ? "passed" : "failed"; + if (receipt.status !== expectedStatus) { + throw new Error("Native check receipt status is inconsistent with its evidence"); + } + if (receipt.status === "passed" && (counts.filesSelected > NATIVE_CHECK_LIMITS.maxFiles || counts.filesScanned + counts.binaryFilesSkipped !== counts.filesSelected)) { + throw new Error("Passed Native check receipt must cover every selected file"); + } + if (counts.filesSelected > NATIVE_CHECK_LIMITS.maxFiles && !issues.some((issue) => issue.kind === "scan-limit")) { + throw new Error("Native check receipt exceeds its file budget without a scan-limit issue"); + } + const content = { + schema: NATIVE_CHECK_RECEIPT_SCHEMA, + change: receipt.change, + sourceRevision, + checker, + inputHash: expectedInputHash, + status: receipt.status, + startedAt, + endedAt, + contract, + implementation, + counts, + issues, + issuesTruncated: receipt.issuesTruncated, + stale: receipt.stale, + staleReasons: staleReasons2 + }; + const receiptHash = hash5(receipt.receiptHash, "Native check receipt content hash"); + if (canonicalHash(NATIVE_CHECK_RECEIPT_HASH_TAG, content) !== receiptHash) { + throw new Error("Native check receipt content hash mismatch"); + } + return { ...content, receiptHash }; +} +function buildNativeCheckReceipt(input) { + const checker = { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + hash: NATIVE_CHECKER_HASH, + limits: { ...NATIVE_CHECK_LIMITS } + }; + const content = { + schema: NATIVE_CHECK_RECEIPT_SCHEMA, + ...input, + checker, + inputHash: nativeCheckInputHash({ + change: input.change, + sourceRevision: input.sourceRevision, + checkerHash: checker.hash, + contractHash: input.contract.expectedHash, + scopeHash: input.implementation.scopeHash, + snapshotHash: input.implementation.expectedSnapshotHash + }) + }; + return parseNativeCheckReceipt({ + ...content, + receiptHash: canonicalHash(NATIVE_CHECK_RECEIPT_HASH_TAG, content) + }); +} + +// domains/comet-native/native-check-receipt-storage.ts +var HASH_PATTERN11 = /^[a-f0-9]{64}$/u; +var RECEIPT_REF_PATTERN = /^runtime\/evidence\/check-receipts\/([a-f0-9]{64})\.json$/u; +var MAX_NATIVE_CHECK_RECEIPT_BYTES = 512 * 1024; +function sameDirectoryIdentity6(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity7(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryChain5(root, directory) { + const lexicalRoot = path26.resolve(root); + const lexicalDirectory = path26.resolve(directory); + if (!isInsidePath(lexicalRoot, lexicalDirectory)) { + throw new Error("Native check receipt parent is outside the Native root"); + } + const chain = []; + let cursor = lexicalRoot; + for (const segment of [ + "", + ...path26.relative(lexicalRoot, lexicalDirectory).split(path26.sep).filter(Boolean) + ]) { + if (segment) cursor = path26.join(cursor, segment); + const stat = await fs17.lstat(cursor); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native check receipt parent must be a real directory: ${cursor}`); + } + const realPath = await fs17.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new Error("Native check receipt parent resolves outside the Native root"); + } + chain.push({ + path: cursor, + realPath, + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }); + } + return chain; +} +async function verifyDirectoryChain6(chain) { + for (const identity of chain) { + const stat = await fs17.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity6(identity, stat) || await fs17.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native check receipt parent changed while reading: ${identity.path}`); + } + } +} +function nativeCheckReceiptRef(hash6) { + if (!HASH_PATTERN11.test(hash6)) throw new Error("Native check receipt hash is invalid"); + return `runtime/evidence/check-receipts/${hash6}.json`; +} +function receiptHashFromRef(ref) { + const match = RECEIPT_REF_PATTERN.exec(ref); + if (!match) throw new Error("Native check receipt ref is invalid"); + return match[1]; +} +function receiptFile(paths, name, hash6) { + return path26.join(nativeChangeDir(paths, name), ...nativeCheckReceiptRef(hash6).split("/")); +} +async function readBoundedReceipt(file, changeRoot, nativeRoot) { + const chain = await captureDirectoryChain5(nativeRoot, path26.dirname(file)); + const before = await fs17.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error("Native check receipt must be a regular file"); + } + if (before.size > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + const [realChangeRoot, beforeRealPath] = await Promise.all([ + fs17.realpath(changeRoot), + fs17.realpath(file) + ]); + if (!isInsidePath(realChangeRoot, beforeRealPath)) { + throw new Error("Native check receipt resolves outside its change"); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants4.O_RDONLY | fsConstants4.O_NOFOLLOW | fsConstants4.O_NONBLOCK; + const handle = await fs17.open(file, openFlags).catch((error) => { + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new Error("Native check receipt became unsafe while opening"); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs17.lstat(file), + fs17.realpath(file) + ]); + await verifyDirectoryChain6(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity7(before, opened) || !sameFileIdentity7(opened, pathAfterOpen)) { + throw new Error("Native check receipt changed while opening"); + } + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(16 * 1024); + while (true) { + const remaining = MAX_NATIVE_CHECK_RECEIPT_BYTES + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs17.lstat(file), + fs17.realpath(file) + ]); + await verifyDirectoryChain6(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !isInsidePath(realChangeRoot, afterRealPath) || !sameFileIdentity7(opened, afterHandle) || !sameFileIdentity7(opened, afterPath)) { + throw new Error("Native check receipt changed while reading"); + } + return JSON.parse(Buffer.concat(chunks, total).toString("utf8")); + } finally { + await handle.close(); + } +} +async function readNativeCheckReceipt(paths, name, ref) { + const expectedHash = receiptHashFromRef(ref); + const changeRoot = nativeChangeDir(paths, name); + const file = receiptFile(paths, name, expectedHash); + await resolveContainedNativePath(paths.nativeRoot, file); + const receipt = parseNativeCheckReceipt( + await readBoundedReceipt(file, changeRoot, paths.nativeRoot) + ); + if (receipt.change !== name || receipt.receiptHash !== expectedHash) { + throw new Error("Native check receipt ref/hash/change mismatch"); + } + return receipt; +} +async function writeNativeCheckReceipt(options) { + const receipt = parseNativeCheckReceipt(options.receipt); + if (receipt.change !== options.name) { + throw new Error("Native check receipt change mismatch"); + } + const ref = nativeCheckReceiptRef(receipt.receiptHash); + const file = receiptFile(options.paths, options.name, receipt.receiptHash); + const serializedBytes = Buffer.byteLength(JSON.stringify(receipt, null, 2) + "\n", "utf8"); + if (serializedBytes > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + await resolveContainedNativePath(options.paths.nativeRoot, file); + try { + const existing = await readNativeCheckReceipt(options.paths, options.name, ref); + if (JSON.stringify(existing) !== JSON.stringify(receipt)) { + throw new Error(`Native check receipt hash collision for ${receipt.receiptHash}`); + } + return ref; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, receipt, { containedRoot: options.paths.nativeRoot }); + const persisted = await readNativeCheckReceipt(options.paths, options.name, ref); + if (JSON.stringify(persisted) !== JSON.stringify(receipt)) { + throw new Error("Native check receipt changed while being persisted"); + } + return ref; +} + +// domains/comet-native/native-contract.ts +import path27 from "node:path"; +var CONTRACT_HASH_TAG = "comet.native.contract.v1"; +var ACCEPTANCE_SET_HASH_TAG = "comet.native.acceptance-set.v1"; +var HASH_PATTERN12 = /^[a-f0-9]{64}$/u; +var NATIVE_CONTRACT_LIMITS = { + maxAcceptanceCriteria: NATIVE_ACCEPTANCE_LIMITS.maxCriteria +}; +function portableRef2(value, label) { + const normalized = path27.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path27.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized project-relative ref`); + } + return value; +} +function contentHash(value) { + return canonicalHash("comet.native.contract-content.v1", value.replace(/\r\n?/gu, "\n")); +} +function normalizeSpec3(input) { + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(input.capability)) { + throw new Error(`Invalid Native contract capability: ${input.capability}`); + } + if (input.operation !== "create" && input.operation !== "replace" && input.operation !== "remove") { + throw new Error(`Invalid Native contract operation for ${input.capability}`); + } + if (input.operation === "remove") { + if (input.source !== null || input.markdown !== null || !input.baseHash?.match(HASH_PATTERN12)) { + throw new Error(`Remove contract ${input.capability} requires only a base hash`); + } + return { + snapshot: { + capability: input.capability, + operation: "remove", + source: null, + baseHash: input.baseHash, + contentHash: null + }, + markdown: null + }; + } + if (input.source === null || input.markdown === null) { + throw new Error(`${input.operation} contract ${input.capability} requires source content`); + } + if (input.operation === "create" && input.baseHash !== null) { + throw new Error(`Create contract ${input.capability} requires a null base hash`); + } + if (input.operation === "replace" && !input.baseHash?.match(HASH_PATTERN12)) { + throw new Error(`Replace contract ${input.capability} requires a base hash`); + } + const source = portableRef2(input.source, `Contract source for ${input.capability}`); + return { + snapshot: { + capability: input.capability, + operation: input.operation, + source, + baseHash: input.baseHash, + contentHash: contentHash(input.markdown) + }, + markdown: input.markdown + }; +} +function compareCriteria(left, right) { + return compareText7(left.id, right.id); +} +function compareText7(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function buildNativeContractSnapshot(input) { + const briefSource = portableRef2(input.briefSource ?? "brief.md", "Native brief source"); + const specs = input.specs.map(normalizeSpec3).sort((left, right) => compareText7(left.snapshot.capability, right.snapshot.capability)); + const capabilities = specs.map(({ snapshot: snapshot2 }) => snapshot2.capability); + if (new Set(capabilities).size !== capabilities.length) { + throw new Error("Native contract contains duplicate capabilities"); + } + const sources = [ + briefSource, + ...specs.flatMap(({ snapshot: snapshot2 }) => snapshot2.source === null ? [] : [snapshot2.source]) + ]; + if (new Set(sources).size !== sources.length) { + throw new Error("Native contract contains duplicate artifact sources"); + } + const acceptance = deriveBriefAcceptanceCriteria( + input.briefMarkdown, + briefSource, + NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria + ); + for (const { snapshot: snapshot2, markdown } of specs) { + if (markdown === null || snapshot2.source === null) continue; + acceptance.push( + ...deriveSpecAcceptanceCriteria( + markdown, + snapshot2.source, + NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria - acceptance.length + ) + ); + } + acceptance.sort(compareCriteria); + if (acceptance.length === 0) { + throw new Error("Native contract has no structured acceptance criteria"); + } + if (acceptance.length > NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria) { + throw new Error( + `Native contract exceeds its ${NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria}-criterion acceptance budget` + ); + } + const acceptanceIds = acceptance.map(({ id }) => id); + if (new Set(acceptanceIds).size !== acceptanceIds.length) { + throw new Error("Native contract contains duplicate acceptance criteria"); + } + const brief = { source: briefSource, contentHash: contentHash(input.briefMarkdown) }; + const specSnapshots = specs.map(({ snapshot: snapshot2 }) => snapshot2); + const acceptanceHash = canonicalHash(ACCEPTANCE_SET_HASH_TAG, acceptance); + const contractContent = { + schema: "comet.native.contract.v1", + brief, + specs: specSnapshots, + acceptance, + acceptanceHash + }; + return { + ...contractContent, + contractHash: canonicalHash(CONTRACT_HASH_TAG, contractContent) + }; +} + +// domains/comet-native/native-contract-files.ts +var NATIVE_CONTRACT_FILE_LIMITS = { + maxSpecs: 64, + maxFileBytes: DEFAULT_NATIVE_ARTIFACT_MAX_BYTES, + maxTotalBytes: 4 * 1024 * 1024 +}; +async function collectNativeContractFiles(options) { + if (options.specChanges.length > NATIVE_CONTRACT_FILE_LIMITS.maxSpecs) { + throw new Error("Native contract exceeds its spec-count budget"); + } + const brief = await readNativeBoundedTextFile({ + root: options.changeDir, + ref: options.briefRef, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + let totalBytes = brief.size; + const specs = []; + for (const change of options.specChanges) { + if (change.operation === "remove") { + specs.push({ + capability: change.capability, + operation: "remove", + source: null, + baseHash: change.base_hash, + markdown: null + }); + continue; + } + if (!change.source) { + throw new Error(`Native contract ${change.capability} has no proposed spec source`); + } + const source = await readNativeBoundedTextFile({ + root: options.changeDir, + ref: change.source, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + totalBytes += source.size; + if (totalBytes > NATIVE_CONTRACT_FILE_LIMITS.maxTotalBytes) { + throw new Error("Native contract exceeds its total byte budget"); + } + specs.push({ + capability: change.capability, + operation: change.operation, + source: source.ref, + baseHash: change.base_hash, + markdown: source.text + }); + } + return { + contract: buildNativeContractSnapshot({ + briefSource: brief.ref, + briefMarkdown: brief.text, + specs + }), + sourceCount: specs.filter((spec) => spec.source !== null).length + 1, + totalBytes + }; +} + +// domains/comet-native/native-verification-runtime.ts +function projectionManifest(projection) { + return { + schema: "comet.native.content-snapshot.v1", + origin: projection.origin, + createdAt: "1970-01-01T00:00:00.000Z", + complete: projection.complete, + limits: projection.limits, + ...projection.policy ? { policy: projection.policy } : {}, + entries: projection.entries, + omitted: projection.omitted, + omittedCount: projection.omittedCount, + ...projection.omissionOverflow ? { omissionOverflow: projection.omissionOverflow } : {} + }; +} +function nativeRootRef2(paths) { + const value = path28.relative(paths.projectRoot, paths.nativeRoot).replaceAll("\\", "/"); + if (!value || value === ".." || value.startsWith("../") || path28.posix.isAbsolute(value)) { + throw new Error("Native root is outside the project root"); + } + return value; +} +async function currentProjectionHash(options) { + const baseline = projectionManifest(options.bundle.baseline); + const current = await createNativeCurrentContentSnapshot(options.paths, baseline, { + origin: "explicit", + now: options.now + }); + return buildNativeImplementationScopeBundle({ + baseline, + current, + contractHash: options.bundle.scope.contractHash, + declaredArtifacts: options.bundle.scope.declaredArtifacts, + noCodeReason: options.bundle.scope.noCodeReason + }).scope.currentProjectionHash; +} +async function inspectCurrentScopeFacts(options) { + if (!options.state.implementation_scope) { + throw new Error("Native change has no implementation scope"); + } + const bundle = await readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ); + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }); + const currentHash = await currentProjectionHash({ + paths: options.paths, + bundle, + now: options.now + }); + const findingCodes = []; + if (contract.contract.contractHash !== bundle.scope.contractHash) { + findingCodes.push("verification-contract-stale"); + } + if (currentHash !== bundle.scope.currentProjectionHash) { + findingCodes.push("verification-implementation-stale"); + } + return { + bundle, + contract: contract.contract, + contractHash: contract.contract.contractHash, + acceptanceHash: contract.contract.acceptanceHash, + findingCodes + }; +} +async function inspectNativeImplementationScopeFreshness(options) { + if (!options.state.implementation_scope) { + return { freshness: "missing", findingCodes: ["verification-evidence-missing"] }; + } + try { + const facts = await inspectCurrentScopeFacts(options); + const findingCodes = [...new Set(facts.findingCodes)].sort(); + return { + freshness: findingCodes.length === 0 ? "fresh" : "stale", + findingCodes + }; + } catch { + return { freshness: "invalid", findingCodes: ["verification-evidence-invalid"] }; + } +} +async function reportEvidence(options) { + const report = await readNativeBoundedTextFile({ + root: nativeChangeDir(options.paths, options.state.name), + ref: options.reportRef + }); + return { + ref: report.ref, + hash: report.hash, + text: report.text, + entries: parseNativeVerificationMachineBlock(report.text) + }; +} +function checkReceiptBindingCodes(options) { + const { receipt, implementationScope } = options; + const codes = []; + const selectedFiles = implementationScope.scope.changes.filter((change) => change.after !== null); + const selectedBytes = selectedFiles.reduce((total, change) => total + change.after.size, 0); + if (receipt.stale || receipt.sourceRevision !== options.sourceRevision || receipt.contract.expectedHash !== options.contractHash || receipt.contract.beforeHash !== options.contractHash || receipt.contract.afterHash !== options.contractHash || receipt.implementation.scopeHash !== implementationScope.scope.scopeHash || receipt.implementation.expectedSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.implementation.beforeSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.implementation.afterSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.counts.filesSelected !== selectedFiles.length || receipt.status === "passed" && (receipt.counts.filesScanned + receipt.counts.binaryFilesSkipped !== selectedFiles.length || receipt.counts.bytesScanned !== selectedBytes)) { + codes.push("verification-receipt-stale"); + } + if (options.result === "pass" && receipt.status !== "passed") { + codes.push("verification-receipt-outcome-mismatch"); + } + return codes; +} +async function inspectNativeVerificationEvidence(options) { + if (options.state.phase !== "verify") { + throw new Error(`Native verification evidence requires Verify, got ${options.state.phase}`); + } + const facts = await inspectCurrentScopeFacts(options); + if (facts.findingCodes.length > 0) { + return { + ready: false, + findingCodes: facts.findingCodes, + envelope: null, + evidenceRef: null, + reportSnapshot: null + }; + } + const report = await reportEvidence(options); + let receiptRef = null; + if (options.receiptRef) { + const receipt = await readNativeCheckReceipt( + options.paths, + options.state.name, + options.receiptRef + ); + const receiptCodes = checkReceiptBindingCodes({ + receipt, + sourceRevision: options.state.revision, + result: options.result, + contractHash: facts.contractHash, + implementationScope: facts.bundle + }); + if (receiptCodes.length > 0) { + throw new Error(`Native verification receipt is not admissible: ${receiptCodes.join(", ")}`); + } + receiptRef = options.receiptRef; + } + const trace = buildNativeAcceptanceEvidenceTrace(facts.contract.acceptance, report.entries, { + nativeRootRef: nativeRootRef2(options.paths) + }); + const allowance = options.state.partial_allowance ? await readNativePartialAllowance( + options.paths, + options.state.name, + options.state.partial_allowance + ) : null; + const envelope = buildNativeVerificationEvidenceEnvelope({ + change: options.state.name, + sourceRevision: options.state.revision, + result: options.result, + contractHash: facts.contractHash, + acceptanceHash: facts.acceptanceHash, + implementationScope: { + ref: options.state.implementation_scope, + bundle: facts.bundle + }, + reportRef: report.ref, + reportHash: report.hash, + receiptRef, + acceptanceTrace: trace, + partialAllowance: options.state.partial_allowance && allowance ? { ref: options.state.partial_allowance, allowance } : null, + now: options.now + }); + const evidenceRef = nativeEvidenceRef("verifications", envelope.envelopeHash); + return { + ready: true, + findingCodes: [], + envelope, + evidenceRef, + reportSnapshot: { hash: report.hash, text: report.text } + }; +} +async function persistNativeVerificationEvidence(options) { + if (!options.preparation.ready || options.preparation.envelope === null || options.preparation.evidenceRef === null || options.preparation.reportSnapshot === null) { + throw new Error("Native verification evidence is not ready to persist"); + } + await writeNativeVerificationReportSnapshot({ + paths: options.paths, + name: options.state.name, + ...options.preparation.reportSnapshot + }); + const evidenceRef = await writeNativeVerificationEvidence({ + paths: options.paths, + name: options.state.name, + evidence: options.preparation.envelope + }); + if (evidenceRef !== options.preparation.evidenceRef) { + throw new Error("Native verification evidence persistence ref changed"); + } +} +function emptyEvidence(result2, freshness) { + return { + result: result2, + freshness, + contractHash: null, + acceptanceHash: null, + implementationScopeHash: null, + reportHash: null, + envelopeHash: null, + partialAllowanceHash: null, + skippedAcceptanceCount: 0 + }; +} +async function inspectNativeVerificationFreshness(options) { + if (!options.state.implementation_scope || !options.state.verification_evidence || !options.state.verification_report) { + return { + freshness: "missing", + findingCodes: ["verification-evidence-missing"], + evidence: emptyEvidence(options.state.verification_result, "missing"), + envelope: null + }; + } + try { + const [facts, envelope, report] = await Promise.all([ + inspectCurrentScopeFacts(options), + readNativeVerificationEvidence( + options.paths, + options.state.name, + options.state.verification_evidence + ), + reportEvidence({ + paths: options.paths, + state: options.state, + reportRef: options.state.verification_report + }) + ]); + const findingCodes = [...facts.findingCodes]; + if (report.hash !== envelope.reportHash || report.ref !== envelope.reportRef) { + findingCodes.push("verification-report-stale"); + } + if (envelope.result !== options.state.verification_result || envelope.implementationScopeRef !== options.state.implementation_scope || envelope.partialAllowanceRef !== options.state.partial_allowance || envelope.sourceRevision >= options.state.revision || envelope.contractHash !== facts.bundle.scope.contractHash || envelope.acceptanceCriteriaHash !== facts.acceptanceHash) { + findingCodes.push("verification-state-mismatch"); + } + if (envelope.receiptRef) { + try { + const receipt = await readNativeCheckReceipt( + options.paths, + options.state.name, + envelope.receiptRef + ); + findingCodes.push( + ...checkReceiptBindingCodes({ + receipt, + sourceRevision: envelope.sourceRevision, + result: envelope.result, + contractHash: envelope.contractHash, + implementationScope: facts.bundle + }) + ); + } catch { + findingCodes.push("verification-receipt-invalid"); + } + } + const uniqueCodes = [...new Set(findingCodes)].sort(); + const freshness = uniqueCodes.length > 0 ? "stale" : envelope.freshness; + return { + freshness, + findingCodes: uniqueCodes, + evidence: { + result: options.state.verification_result, + freshness, + contractHash: envelope.contractHash, + acceptanceHash: envelope.acceptanceCriteriaHash, + implementationScopeHash: envelope.implementationScopeHash, + reportHash: envelope.reportHash, + envelopeHash: envelope.envelopeHash, + partialAllowanceHash: envelope.partialAllowanceHash, + skippedAcceptanceCount: envelope.acceptanceTrace.skipped + }, + envelope + }; + } catch { + return { + freshness: "invalid", + findingCodes: ["verification-evidence-invalid"], + evidence: emptyEvidence(options.state.verification_result, "invalid"), + envelope: null + }; + } +} + +// domains/comet-native/native-archive-inspection.ts +function archiveTargetRef(name, now) { + return `archive/${now.toISOString().slice(0, 10)}-${name}`; +} +async function exists2(target) { + try { + await fs18.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function optionalBoundedHash(root, ref) { + try { + return (await readNativeBoundedTextFile({ root, ref })).hash; + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function specFact(paths, name, change) { + const canonical = canonicalSpecPath(paths, change.capability); + if (!isInsidePath(paths.nativeRoot, canonical)) { + throw new Error(`Native canonical spec escapes its root: ${change.capability}`); + } + const canonicalRef = path29.relative(paths.nativeRoot, canonical).replaceAll("\\", "/"); + const actualBaseHash = await optionalBoundedHash(paths.nativeRoot, canonicalRef); + let proposedHash = null; + if (change.operation !== "remove") { + if (!change.source) throw new Error(`Native proposed spec is missing: ${change.capability}`); + proposedHash = (await readNativeBoundedTextFile({ + root: nativeChangeDir(paths, name), + ref: change.source + })).hash; + } + return { + capability: change.capability, + operation: change.operation, + expectedBaseHash: change.operation === "create" ? null : change.base_hash, + actualBaseHash, + proposedHash + }; +} +async function hasPendingTransition(paths, name) { + return exists2(nativeTransitionJournalFile(paths, name)); +} +async function inspectNativeArchivePreflight(options) { + const now = options.now ?? /* @__PURE__ */ new Date(); + const state = await readNativeChange(options.paths, options.name); + const targetRef = archiveTargetRef(state.name, now); + const target = path29.resolve(options.paths.nativeRoot, ...targetRef.split("/")); + if (!isInsidePath(options.paths.nativeRoot, target)) { + throw new Error("Native archive target escapes its root"); + } + const [ + specs, + evidence, + conflicts, + pendingSchema, + pendingCheckpoint, + pendingTransition, + targetExists + ] = await Promise.all([ + Promise.all(state.spec_changes.map((change) => specFact(options.paths, state.name, change))), + inspectNativeVerificationFreshness({ paths: options.paths, state, now }), + inspectNativeChangeConflicts(options.paths, state.name), + hasPendingNativeSchemaMigration(options.paths, state.name), + hasPendingNativeCheckpointRecovery(options.paths, state.name), + hasPendingTransition(options.paths, state.name), + exists2(target) + ]); + return buildNativeArchivePreflight({ + change: state.name, + stateSchema: state.schema, + revision: state.revision, + phase: state.phase, + archived: state.archived, + pendingJournal: pendingSchema || pendingCheckpoint || pendingTransition, + targetRef, + targetExists, + specs, + evidence: evidence.evidence, + findingCodes: [...evidence.findingCodes, ...conflicts.findingCodes] + }); +} + +// domains/comet-native/native-archive-content.ts +import { promises as fs19 } from "node:fs"; +import path30 from "node:path"; +var TREE_HASH_TAG = "comet.native.archive-tree.v1"; +var NATIVE_ARCHIVE_CONTENT_LIMITS = { + maxDepth: 128, + maxEntries: 2e4, + maxFileBytes: 64 * 1024 * 1024, + maxTotalBytes: 256 * 1024 * 1024, + maxManifestBytes: 16 * 1024 * 1024, + maxRefBytes: 4 * 1024 +}; +function normalizedLimits(limits) { + const resolved = { ...NATIVE_ARCHIVE_CONTENT_LIMITS, ...limits }; + for (const [name, value] of Object.entries(resolved)) { + if (!Number.isSafeInteger(value) || value <= 0) { + throw new Error(`Native Archive content limit ${name} must be a positive safe integer`); + } + } + return resolved; +} +function directorySnapshot(entries) { + return JSON.stringify( + entries.map((entry2) => ({ + name: entry2.name, + kind: entry2.isDirectory() ? "directory" : entry2.isFile() ? "file" : entry2.isSymbolicLink() ? "symlink" : "other" + })).sort((left, right) => left.name.localeCompare(right.name)) + ); +} +function appendTreeEntry(entries, entry2, budget, limits) { + budget.entryCount += 1; + if (budget.entryCount > limits.maxEntries) { + throw new Error(`Native Archive content exceeds ${limits.maxEntries} entries`); + } + if (Buffer.byteLength(entry2.ref, "utf8") > limits.maxRefBytes) { + throw new Error(`Native Archive content ref exceeds ${limits.maxRefBytes} bytes: ${entry2.ref}`); + } + const entryBytes = Buffer.byteLength(JSON.stringify(entry2), "utf8"); + budget.manifestBytes += entryBytes + (entries.length === 0 ? 0 : 1); + if (budget.manifestBytes + 2 > limits.maxManifestBytes) { + throw new Error(`Native Archive content manifest exceeds ${limits.maxManifestBytes} bytes`); + } + entries.push(entry2); +} +async function walkArchiveTree(root, directory, entries, budget, limits, depth) { + if (depth > limits.maxDepth) { + throw new Error(`Native Archive content exceeds depth ${limits.maxDepth}`); + } + const protectedDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: `Native Archive content directory ${path30.relative(root, directory) || "."}`, + maxEntries: limits.maxEntries + }); + const beforeSnapshot = directorySnapshot(protectedDirectory.entries); + const children = [...protectedDirectory.entries]; + children.sort((left, right) => left.name.localeCompare(right.name)); + for (const child of children) { + await protectedDirectory.verify(); + const target = path30.join(directory, child.name); + const ref = path30.relative(root, target).replaceAll("\\", "/"); + const stat = await fs19.lstat(target); + if (child.isSymbolicLink() || stat.isSymbolicLink()) { + throw new Error(`Native Archive content must not contain symlinks or junctions: ${ref}`); + } + if (child.isDirectory() && stat.isDirectory()) { + appendTreeEntry(entries, { ref, kind: "directory" }, budget, limits); + await walkArchiveTree(root, target, entries, budget, limits, depth + 1); + continue; + } + if (!child.isFile() || !stat.isFile()) { + throw new Error(`Native Archive content must contain only files and directories: ${ref}`); + } + const snapshot2 = await readNativeProtectedFile({ + root, + file: target, + maxBytes: limits.maxFileBytes, + label: `Native Archive content file ${ref}` + }); + budget.totalBytes += snapshot2.size; + if (budget.totalBytes > limits.maxTotalBytes) { + throw new Error(`Native Archive content exceeds ${limits.maxTotalBytes} total file bytes`); + } + appendTreeEntry( + entries, + { ref, kind: "file", hash: snapshot2.hash, size: snapshot2.size }, + budget, + limits + ); + await protectedDirectory.verify(); + } + await protectedDirectory.verify(); + const afterDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: `Native Archive content directory ${path30.relative(root, directory) || "."}`, + maxEntries: limits.maxEntries + }); + if (directorySnapshot(afterDirectory.entries) !== beforeSnapshot) { + throw new Error( + `Native Archive content directory changed while reading: ${path30.relative(root, directory) || "."}` + ); + } + await Promise.all([protectedDirectory.verify(), afterDirectory.verify()]); +} +async function hashNativeArchiveTree(directory, requestedLimits = {}) { + const limits = normalizedLimits(requestedLimits); + directory = path30.resolve(directory); + const stat = await fs19.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native Archive move source must be a real directory: ${directory}`); + } + const entries = []; + const budget = { entryCount: 0, totalBytes: 0, manifestBytes: 0 }; + await walkArchiveTree(directory, directory, entries, budget, limits, 0); + const manifest = JSON.stringify(entries); + if (Buffer.byteLength(manifest, "utf8") > limits.maxManifestBytes) { + throw new Error(`Native Archive content manifest exceeds ${limits.maxManifestBytes} bytes`); + } + return sha256Text(`${TREE_HASH_TAG}\0${manifest}`); +} +async function inspectNativeArchiveContent(target, requestedLimits = {}) { + const limits = normalizedLimits(requestedLimits); + target = path30.resolve(target); + let stat; + try { + stat = await fs19.lstat(target); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (stat.isSymbolicLink()) { + throw new Error(`Native Archive transaction path must not be a symlink or junction: ${target}`); + } + if (stat.isFile()) { + const snapshot2 = await readNativeProtectedFile({ + root: path30.dirname(target), + file: target, + maxBytes: limits.maxFileBytes, + label: `Native Archive transaction file ${path30.basename(target)}` + }); + return { kind: "file", hash: snapshot2.hash }; + } + if (stat.isDirectory()) { + return { kind: "directory", hash: await hashNativeArchiveTree(target, limits) }; + } + throw new Error(`Native Archive transaction path has an unsupported file type: ${target}`); +} + +// domains/comet-native/native-archive-transaction.ts +import { promises as fs20 } from "node:fs"; +import path31 from "node:path"; +var NATIVE_ARCHIVE_COPY_MAX_BYTES = 16 * 1024 * 1024; +var NATIVE_ARCHIVE_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_ARCHIVE_CAS_RECORD_MAX_BYTES = 16 * 1024; +var NATIVE_ARCHIVE_CAS_SCHEMA = "comet.native.archive-cas.v1"; +function resolveRefLexically2(paths, ref) { + const target = path31.resolve(paths.nativeRoot, ...ref.split("/")); + if (path31.relative(paths.nativeRoot, target).split(path31.sep).includes("..")) { + throw new Error(`Unsafe Native Archive transaction ref: ${ref}`); + } + return target; +} +async function resolveRef2(paths, ref) { + return resolveContainedNativePath(paths.nativeRoot, resolveRefLexically2(paths, ref)); +} +function sameContent(actual, expectedHash, expectedKind = "file") { + return expectedHash === null ? actual === null : actual?.kind === expectedKind && actual.hash === expectedHash; +} +function contentDescription(value) { + return value === null ? "missing" : `${value.kind}:${value.hash}`; +} +async function assertContent(options) { + const actual = await inspectNativeArchiveContent(options.target); + if (!sameContent(actual, options.expectedHash, options.expectedKind)) { + throw new Error( + `${options.label} content changed: expected ${options.expectedHash ?? "missing"}, got ${contentDescription(actual)}` + ); + } + return actual; +} +async function createNativeArchiveTransactionV2(paths, journal) { + const validated = parseNativeArchiveTransactionJournalV2(journal); + const tx = await resolveNativeTransactionPaths(paths, validated.id); + await fs20.mkdir(tx.staged, { recursive: true }); + await fs20.mkdir(tx.backups, { recursive: true }); + await atomicWriteJson(tx.journal, validated, { containedRoot: paths.nativeRoot }); + await appendNativeTransactionEvent(paths, validated.id, "prepared"); +} +async function readNativeArchiveTransactionV2(paths, id) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.journal, + maxBytes: NATIVE_ARCHIVE_JOURNAL_MAX_BYTES, + label: `Native Archive transaction journal ${id}` + }); + const journal = parseNativeArchiveTransactionJournalV2( + JSON.parse(snapshot2.bytes.toString("utf8")) + ); + if (journal.id !== id) throw new Error(`Invalid Native Archive transaction journal: ${id}`); + return journal; +} +async function setStatus(paths, journal, status) { + const updated = parseNativeArchiveTransactionJournalV2({ ...journal, status }); + const tx = await resolveNativeTransactionPaths(paths, updated.id); + await atomicWriteJson(tx.journal, updated, { containedRoot: paths.nativeRoot }); + return updated; +} +function fileObjectIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +function fileVersion(stat) { + return { + ...fileObjectIdentity(stat), + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileObject2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs; +} +function sameFileVersion(expected, actual) { + return sameFileObject2(expected, actual) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function pathExists(target) { + try { + await fs20.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function captureStableArchiveFile(options) { + const before = await fs20.lstat(options.file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + const beforeVersion = fileVersion(before); + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: options.file, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: options.label + }); + const after = await fs20.lstat(options.file); + if (!after.isFile() || after.isSymbolicLink() || !sameFileVersion(beforeVersion, fileVersion(after)) || snapshot2.hash !== options.expectedHash) { + throw new Error(`${options.label} content changed or object identity changed while binding`); + } + return { identity: fileObjectIdentity(after), size: snapshot2.size }; +} +function parseCasRecord(value, operation, role, expectedHash) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Archive CAS ${operation.id} ${role} record must be an object`); + } + const record8 = value; + const keys = Object.keys(record8).sort(); + if (keys.join(",") !== "hash,identity,operationId,role,schema") { + throw new Error(`Archive CAS ${operation.id} ${role} record has an invalid shape`); + } + const identity = record8.identity; + if (!identity || typeof identity !== "object" || Array.isArray(identity)) { + throw new Error(`Archive CAS ${operation.id} ${role} identity must be an object`); + } + const identityRecord = identity; + if (Object.keys(identityRecord).sort().join(",") !== "birthtimeMs,dev,ino") { + throw new Error(`Archive CAS ${operation.id} ${role} identity has an invalid shape`); + } + for (const field2 of ["dev", "ino", "birthtimeMs"]) { + if (typeof identityRecord[field2] !== "number" || !Number.isFinite(identityRecord[field2]) || identityRecord[field2] < 0) { + throw new Error(`Archive CAS ${operation.id} ${role} identity ${field2} is invalid`); + } + } + if (record8.schema !== NATIVE_ARCHIVE_CAS_SCHEMA || record8.operationId !== operation.id || record8.role !== role || record8.hash !== expectedHash) { + throw new Error(`Archive CAS ${operation.id} ${role} record is not transaction-bound`); + } + return { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: operation.id, + role, + hash: expectedHash, + identity: { + dev: identityRecord.dev, + ino: identityRecord.ino, + birthtimeMs: identityRecord.birthtimeMs + } + }; +} +async function archiveCasPaths(paths, journal, operation, target) { + const tx = await resolveNativeTransactionPaths(paths, journal.id); + const directory = path31.join(tx.backups, ".cas"); + const prefix = `.${path31.basename(target)}.comet-archive-cas-${journal.id}-${operation.id}`; + const result2 = { + directory, + originalRecord: path31.join(directory, `${operation.id}.original.json`), + postRecord: path31.join(directory, `${operation.id}.post.json`), + candidate: path31.join(directory, `${operation.id}.candidate`), + originalQuarantine: path31.join(path31.dirname(target), `${prefix}.original`), + rollbackQuarantine: path31.join(path31.dirname(target), `${prefix}.rollback`) + }; + await Promise.all( + Object.values(result2).map((item) => resolveContainedNativePath(paths.nativeRoot, item)) + ); + return result2; +} +async function readCasRecord(options) { + if (!await pathExists(options.file)) return null; + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: options.file, + maxBytes: NATIVE_ARCHIVE_CAS_RECORD_MAX_BYTES, + label: `Archive CAS ${options.operation.id} ${options.role} record` + }); + return parseCasRecord( + JSON.parse(snapshot2.bytes.toString("utf8")), + options.operation, + options.role, + options.expectedHash + ); +} +async function persistCasRecord(options) { + const existing = await readCasRecord(options); + if (existing) { + if (!sameFileObject2(existing.identity, options.identity)) { + throw new Error( + `Archive CAS ${options.operation.id} ${options.role} object identity changed` + ); + } + return existing; + } + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.file), + label: `Archive CAS ${options.operation.id} records` + }); + const record8 = { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: options.role, + hash: options.expectedHash, + identity: options.identity + }; + try { + await atomicWriteJson(options.file, record8, { + containedRoot: options.paths.nativeRoot, + exclusive: true + }); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const persisted = await readCasRecord(options); + if (!persisted || !sameFileObject2(persisted.identity, options.identity)) { + throw new Error(`Archive CAS ${options.operation.id} ${options.role} record changed`); + } + return persisted; +} +async function validateFileAgainstCasRecord(options) { + const current = await captureStableArchiveFile({ + paths: options.paths, + file: options.file, + expectedHash: options.record.hash, + label: options.label + }); + if (!sameFileObject2(options.record.identity, current.identity)) { + throw new Error(`${options.label} object identity changed`); + } +} +async function bindCurrentTarget(options) { + const current = await captureStableArchiveFile({ + paths: options.paths, + file: options.file, + expectedHash: options.expectedHash, + label: options.label + }); + const record8 = await persistCasRecord({ + paths: options.paths, + file: options.recordFile, + operation: options.operation, + role: options.role, + expectedHash: options.expectedHash, + identity: current.identity + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.file, + operation: options.operation, + record: record8, + label: options.label + }); + return record8; +} +async function restoreUnexpectedQuarantine(options) { + if (!await pathExists(options.quarantine) || await pathExists(options.target)) return; + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive CAS ${options.operation.id} quarantine restoration` + }); + await guard.verify(); + try { + await fs20.link(options.quarantine, options.target); + } catch (error) { + if (error.code === "EEXIST") return; + throw error; + } + await guard.verify(); + const [quarantine, target] = await Promise.all([ + fs20.lstat(options.quarantine), + fs20.lstat(options.target) + ]); + if (!sameFileObject2(fileObjectIdentity(quarantine), fileObjectIdentity(target))) { + throw new Error(`Archive CAS ${options.operation.id} could not restore quarantined content`); + } + await fs20.unlink(options.quarantine); + await guard.verify(); +} +async function quarantineBoundTarget(options) { + if (await pathExists(options.quarantine)) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} quarantine ${options.operation.target}` + }); + return; + } + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive ${options.phase} target ${options.operation.target}` + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} target ${options.operation.target}` + }); + await options.hooks?.afterArchiveTargetBound?.(options.phase, options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} target ${options.operation.target}` + }); + try { + await fs20.rename(options.target, options.quarantine); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} quarantine ${options.operation.target}` + }); + } catch (error) { + await restoreUnexpectedQuarantine(options); + throw new Error( + `Archive ${options.phase} target ${options.operation.target} changed during quarantine`, + { cause: error } + ); + } + await options.hooks?.afterArchiveTargetQuarantined?.( + options.phase, + options.operation, + options.quarantine + ); +} +async function removeExactCasFile(options) { + if (!await pathExists(options.file)) return; + await validateFileAgainstCasRecord(options); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.file), + label: options.label + }); + await guard.verify(); + await validateFileAgainstCasRecord(options); + await fs20.unlink(options.file); + await guard.verify(); +} +async function ensureBackup(paths, operation, hooks) { + if (!operation.backup || operation.expectedTargetHash === null) return; + const target = await resolveRef2(paths, operation.target); + const backup = await resolveRef2(paths, operation.backup); + const existing = await inspectNativeArchiveContent(backup); + if (existing !== null) { + if (!sameContent(existing, operation.expectedTargetHash)) { + throw new Error(`Archive transaction backup content changed: ${operation.backup}`); + } + return; + } + await assertContent({ + target, + expectedHash: operation.expectedTargetHash, + label: `Archive transaction target ${operation.target}` + }); + await copyNativeProtectedFile({ + sourceRoot: paths.nativeRoot, + source: target, + targetRoot: paths.nativeRoot, + target: backup, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: `Archive transaction backup ${operation.backup}`, + expectedHash: operation.expectedTargetHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => hooks?.afterProtectedCopySourceParentCaptured?.("backup", operation.target) + } + }); + await assertContent({ + target: backup, + expectedHash: operation.expectedTargetHash, + label: `Archive transaction backup ${operation.backup}` + }); +} +async function ensureOriginalTargetQuarantined(options) { + const expectedHash = options.operation.expectedTargetHash; + if (expectedHash === null) { + throw new Error( + `Archive operation ${options.operation.id} has no original target to quarantine` + ); + } + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + let record8 = await readCasRecord({ + paths: options.paths, + file: cas.originalRecord, + operation: options.operation, + role: "original", + expectedHash + }); + if (await pathExists(cas.originalQuarantine)) { + if (!record8) { + throw new Error( + `Archive apply quarantine ${options.operation.target} has no bound object identity` + ); + } + await validateFileAgainstCasRecord({ + paths: options.paths, + file: cas.originalQuarantine, + operation: options.operation, + record: record8, + label: `Archive apply quarantine ${options.operation.target}` + }); + await ensureBackup(options.paths, options.operation, options.hooks); + return { record: record8, quarantine: cas.originalQuarantine }; + } + if (!await pathExists(options.target)) { + throw new Error( + `Archive apply target ${options.operation.target} disappeared before quarantine` + ); + } + record8 ??= await bindCurrentTarget({ + paths: options.paths, + file: options.target, + recordFile: cas.originalRecord, + operation: options.operation, + role: "original", + expectedHash, + label: `Archive apply target ${options.operation.target}` + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: record8, + label: `Archive apply target ${options.operation.target}` + }); + await ensureBackup(options.paths, options.operation, options.hooks); + await quarantineBoundTarget({ + paths: options.paths, + operation: options.operation, + phase: "apply", + target: options.target, + quarantine: cas.originalQuarantine, + record: record8, + hooks: options.hooks + }); + return { record: record8, quarantine: cas.originalQuarantine }; +} +async function ensureWriteCandidate(options) { + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + if (await pathExists(cas.candidate)) { + await captureStableArchiveFile({ + paths: options.paths, + file: cas.candidate, + expectedHash: options.operation.stagedHash, + label: `Archive write candidate ${options.operation.target}` + }); + return cas.candidate; + } + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: cas.directory, + label: `Archive CAS ${options.operation.id} records` + }); + await copyNativeProtectedFile({ + sourceRoot: options.paths.nativeRoot, + source: options.staged, + targetRoot: options.paths.nativeRoot, + target: cas.candidate, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: `Archive transaction staged file ${options.operation.staged}`, + expectedHash: options.operation.stagedHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => options.hooks?.afterProtectedCopySourceParentCaptured?.("apply", options.operation.staged) + } + }); + return cas.candidate; +} +async function ensureWriteInstalled(options) { + const expectedHash = options.operation.stagedHash; + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + let record8 = await readCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash + }); + if (record8 && await pathExists(options.target)) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: record8, + label: `Archive write target ${options.operation.target}` + }); + if (await pathExists(cas.candidate)) { + await removeExactCasFile({ + paths: options.paths, + file: cas.candidate, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + } + return record8; + } + if (await pathExists(options.target)) { + const candidate2 = await ensureWriteCandidate(options); + const candidateIdentity2 = await captureStableArchiveFile({ + paths: options.paths, + file: candidate2, + expectedHash, + label: `Archive write candidate ${options.operation.target}` + }); + const targetIdentity2 = await captureStableArchiveFile({ + paths: options.paths, + file: options.target, + expectedHash, + label: `Archive write target ${options.operation.target}` + }); + if (!sameFileObject2(candidateIdentity2.identity, targetIdentity2.identity)) { + throw new Error( + `Archive write target ${options.operation.target} is occupied by an external object` + ); + } + record8 = await persistCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash, + identity: targetIdentity2.identity + }); + await removeExactCasFile({ + paths: options.paths, + file: candidate2, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + return record8; + } + const candidate = await ensureWriteCandidate(options); + const candidateIdentity = await captureStableArchiveFile({ + paths: options.paths, + file: candidate, + expectedHash, + label: `Archive write candidate ${options.operation.target}` + }); + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive write target ${options.operation.target}` + }); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive write target ${options.operation.target}` + }); + await options.hooks?.beforeArchiveTargetInstall?.("apply", options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: candidate, + operation: options.operation, + record: { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: "post", + hash: expectedHash, + identity: candidateIdentity.identity + }, + label: `Archive write candidate ${options.operation.target}` + }); + try { + await fs20.link(candidate, options.target); + } catch (error) { + if (error.code === "EEXIST") { + throw new Error( + `Archive write target ${options.operation.target} was created before exclusive install`, + { cause: error } + ); + } + throw error; + } + await guard.verify(); + await options.hooks?.afterArchiveTargetInstalled?.("apply", options.operation, options.target); + const targetIdentity = await captureStableArchiveFile({ + paths: options.paths, + file: options.target, + expectedHash, + label: `Archive write target ${options.operation.target}` + }); + if (!sameFileObject2(candidateIdentity.identity, targetIdentity.identity)) { + throw new Error(`Archive write target ${options.operation.target} changed during install`); + } + record8 = await persistCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash, + identity: targetIdentity.identity + }); + await removeExactCasFile({ + paths: options.paths, + file: candidate, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + return record8; +} +async function applyWrite(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + const staged = await resolveRef2(paths, operation.staged); + await assertContent({ + target: staged, + expectedHash: operation.stagedHash, + label: `Archive transaction staged file ${operation.staged}` + }); + if (operation.expectedTargetHash !== null) { + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord || !await pathExists(target)) { + await ensureOriginalTargetQuarantined({ paths, journal, operation, target, hooks }); + } + } else { + const actual = await inspectNativeArchiveContent(target); + if (actual !== null && !sameContent(actual, operation.stagedHash)) { + throw new Error( + `Archive transaction target ${operation.target} changed before create: ${contentDescription(actual)}` + ); + } + } + await ensureWriteInstalled({ + paths, + journal, + operation, + staged, + target, + hooks + }); + await assertContent({ + target, + expectedHash: operation.stagedHash, + label: `Archive transaction target ${operation.target}` + }); +} +async function applyRemove(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + await ensureOriginalTargetQuarantined({ paths, journal, operation, target, hooks }); + await assertContent({ + target, + expectedHash: null, + label: `Archive transaction target ${operation.target}` + }); +} +async function applyMove(paths, operation) { + const source = await resolveRef2(paths, operation.source); + const target = await resolveRef2(paths, operation.target); + const [sourceContent, targetContent] = await Promise.all([ + inspectNativeArchiveContent(source), + inspectNativeArchiveContent(target) + ]); + if (sourceContent === null && sameContent(targetContent, operation.expectedSourceHash, "directory")) { + return; + } + if (!sameContent(sourceContent, operation.expectedSourceHash, "directory") || targetContent !== null) { + throw new Error( + `Archive transaction move ${operation.id} content changed: source=${contentDescription(sourceContent)}, target=${contentDescription(targetContent)}` + ); + } + await fs20.mkdir(path31.dirname(target), { recursive: true }); + await fs20.rename(source, target); + await assertContent({ + target, + expectedHash: operation.expectedSourceHash, + expectedKind: "directory", + label: `Archive transaction move target ${operation.target}` + }); +} +async function applyOperation2(paths, journal, operation, hooks) { + if (operation.type === "write") return applyWrite(paths, journal, operation, hooks); + if (operation.type === "remove") return applyRemove(paths, journal, operation, hooks); + return applyMove(paths, operation); +} +function completedOperationIds(options) { + let operationIndex = 0; + let startedCurrent = false; + const completed = []; + for (const event of options.events) { + if (event.type !== "operation-started" && event.type !== "operation-completed") continue; + const expected = options.journal.operations[operationIndex]; + if (!expected || event.operationId !== expected.id) { + throw new Error( + `Native Archive transaction ${options.journal.id} operation events are out of order` + ); + } + if (event.type === "operation-started") { + startedCurrent = true; + continue; + } + if (!startedCurrent) { + throw new Error( + `Native Archive transaction ${options.journal.id} completed an operation before it started` + ); + } + completed.push(expected.id); + operationIndex += 1; + startedCurrent = false; + } + return completed; +} +async function assertCompletedOperation(paths, journal, operation, finalizationStarted) { + const target = await resolveRef2(paths, operation.target); + if (operation.type === "write") { + const staged = await resolveRef2(paths, operation.staged); + await assertContent({ + target: staged, + expectedHash: operation.stagedHash, + label: `Completed Archive staged file ${operation.staged}` + }); + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord) { + throw new Error(`Completed Archive target ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: target, + operation, + record: postRecord, + label: `Completed Archive target ${operation.target}` + }); + if (operation.expectedTargetHash !== null) { + await assertContent({ + target: await resolveRef2(paths, operation.backup), + expectedHash: operation.expectedTargetHash, + label: `Completed Archive backup ${operation.backup}` + }); + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error( + `Completed Archive target ${operation.target} has no original CAS identity` + ); + } + if (await pathExists(cas.originalQuarantine)) { + await validateFileAgainstCasRecord({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Completed Archive original quarantine ${operation.target}` + }); + } else if (!finalizationStarted) { + throw new Error(`Completed Archive original quarantine ${operation.target} disappeared`); + } + } + return; + } + if (operation.type === "remove") { + await assertContent({ + target, + expectedHash: null, + label: `Completed Archive target ${operation.target}` + }); + await assertContent({ + target: await resolveRef2(paths, operation.backup), + expectedHash: operation.expectedTargetHash, + label: `Completed Archive backup ${operation.backup}` + }); + const cas = await archiveCasPaths(paths, journal, operation, target); + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error(`Completed Archive remove ${operation.target} has no original CAS identity`); + } + if (await pathExists(cas.originalQuarantine)) { + await validateFileAgainstCasRecord({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Completed Archive original quarantine ${operation.target}` + }); + } else if (!finalizationStarted) { + throw new Error(`Completed Archive original quarantine ${operation.target} disappeared`); + } + return; + } + if (finalizationStarted) return; + await assertContent({ + target: await resolveRef2(paths, operation.source), + expectedHash: null, + label: `Completed Archive move source ${operation.source}` + }); + await assertContent({ + target, + expectedHash: operation.expectedSourceHash, + expectedKind: "directory", + label: `Completed Archive move target ${operation.target}` + }); +} +async function applyNativeArchiveTransactionV2(paths, journal, hooks) { + let current = journal.status === "prepared" ? await setStatus(paths, journal, "applying") : journal; + if (current.status !== "applying") { + throw new Error(`Native Archive transaction ${current.id} cannot apply from ${current.status}`); + } + const events = await readNativeTransactionEvents(paths, current.id); + const completedIds = completedOperationIds({ journal: current, events }); + const completed = new Set(completedIds); + const finalizationStarted = events.some((event) => event.type === "archive-finalization-started"); + for (const operation of current.operations.slice(0, completedIds.length)) { + await assertCompletedOperation(paths, current, operation, finalizationStarted); + } + let completedCount = completed.size; + for (const operation of current.operations) { + if (completed.has(operation.id)) continue; + await appendNativeTransactionEvent(paths, current.id, "operation-started", operation.id); + await applyOperation2(paths, current, operation, hooks); + await appendNativeTransactionEvent(paths, current.id, "operation-completed", operation.id); + completedCount += 1; + await hooks?.afterOperation?.(operation, completedCount); + } + current = await readNativeArchiveTransactionV2(paths, current.id); + return current; +} +async function fileMatchesCasRecord(options) { + try { + await validateFileAgainstCasRecord(options); + return true; + } catch { + return false; + } +} +async function installOriginalTarget(options) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive rollback original quarantine ${options.operation.target}` + }); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive rollback target ${options.operation.target}` + }); + await options.hooks?.beforeArchiveTargetInstall?.("rollback", options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive rollback original quarantine ${options.operation.target}` + }); + try { + await fs20.link(options.quarantine, options.target); + } catch (error) { + if (error.code === "EEXIST") { + throw new Error( + `Archive rollback target ${options.operation.target} was created before exclusive restore`, + { cause: error } + ); + } + throw error; + } + await guard.verify(); + await options.hooks?.afterArchiveTargetInstalled?.("rollback", options.operation, options.target); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive rollback target ${options.operation.target}` + }); +} +async function removeWriteCandidateIfPresent(options) { + if (options.operation.type !== "write") return; + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + if (!await pathExists(cas.candidate)) return; + const current = await captureStableArchiveFile({ + paths: options.paths, + file: cas.candidate, + expectedHash: options.operation.stagedHash, + label: `Archive write candidate ${options.operation.target}` + }); + await removeExactCasFile({ + paths: options.paths, + file: cas.candidate, + operation: options.operation, + record: { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: "post", + hash: options.operation.stagedHash, + identity: current.identity + }, + label: `Archive write candidate ${options.operation.target}` + }); +} +async function rollbackWriteOrRemove(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + const cas = await archiveCasPaths(paths, journal, operation, target); + const originalHash = operation.expectedTargetHash; + const originalRecord = originalHash === null ? null : await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: originalHash + }); + let postRecord = operation.type === "write" ? await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }) : null; + if (operation.type === "write" && !postRecord && await pathExists(cas.candidate) && await pathExists(target)) { + const candidate = await captureStableArchiveFile({ + paths, + file: cas.candidate, + expectedHash: operation.stagedHash, + label: `Archive write candidate ${operation.target}` + }); + const current = await captureStableArchiveFile({ + paths, + file: target, + expectedHash: operation.stagedHash, + label: `Archive rollback target ${operation.target}` + }); + if (sameFileObject2(candidate.identity, current.identity)) { + postRecord = await persistCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash, + identity: current.identity + }); + } + } + if (await pathExists(cas.rollbackQuarantine)) { + if (!postRecord) { + throw new Error(`Archive rollback quarantine ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: cas.rollbackQuarantine, + operation, + record: postRecord, + label: `Archive rollback quarantine ${operation.target}` + }); + } + if (await pathExists(target)) { + const alreadyRestored = originalRecord !== null && await fileMatchesCasRecord({ + paths, + file: target, + operation, + record: originalRecord, + label: `Archive rollback target ${operation.target}` + }); + if (!alreadyRestored) { + if (!postRecord) { + if (!originalRecord) { + const actual = await inspectNativeArchiveContent(target); + if (sameContent(actual, originalHash)) { + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + return; + } + } + throw new Error( + `Archive rollback target ${operation.target} is occupied by an external object` + ); + } + await quarantineBoundTarget({ + paths, + operation, + phase: "rollback", + target, + quarantine: cas.rollbackQuarantine, + record: postRecord, + hooks + }); + } + } + if (originalHash === null) { + if (await pathExists(target)) { + throw new Error(`Archive rollback create target ${operation.target} could not be removed`); + } + } else if (!await pathExists(target)) { + if (!originalRecord || !await pathExists(cas.originalQuarantine)) { + throw new Error( + `Archive rollback original target ${operation.target} cannot be recovered safely` + ); + } + await installOriginalTarget({ + paths, + operation, + target, + quarantine: cas.originalQuarantine, + record: originalRecord, + hooks + }); + } + if (originalRecord && await pathExists(cas.originalQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Archive rollback original quarantine ${operation.target}` + }); + } + if (postRecord && await pathExists(cas.rollbackQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.rollbackQuarantine, + operation, + record: postRecord, + label: `Archive rollback quarantine ${operation.target}` + }); + } + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + await assertContent({ + target, + expectedHash: originalHash, + label: `Archive rollback target ${operation.target}` + }); +} +async function rollbackMove(paths, operation) { + const source = await resolveRef2(paths, operation.source); + const target = await resolveRef2(paths, operation.target); + const [sourceContent, targetContent] = await Promise.all([ + inspectNativeArchiveContent(source), + inspectNativeArchiveContent(target) + ]); + if (sameContent(sourceContent, operation.expectedSourceHash, "directory") && targetContent === null) { + return; + } + if (sourceContent !== null || !sameContent(targetContent, operation.expectedSourceHash, "directory")) { + throw new Error( + `Archive rollback move ${operation.id} content changed: source=${contentDescription(sourceContent)}, target=${contentDescription(targetContent)}` + ); + } + await fs20.mkdir(path31.dirname(source), { recursive: true }); + await fs20.rename(target, source); +} +async function rollbackNativeArchiveTransactionV2(paths, journal, hooks) { + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some( + (event) => event.type === "archive-finalization-started" || event.type === "archive-finalized" + )) { + throw new Error("An archive whose finalization started can only be recovered by continuing it"); + } + let current = await setStatus(paths, journal, "rolling-back"); + await appendNativeTransactionEvent(paths, current.id, "rollback-started"); + const started = new Set( + events.filter((event) => event.type === "operation-started" || event.type === "operation-completed").map((event) => event.operationId) + ); + for (const operation of [...current.operations].reverse()) { + if (!started.has(operation.id)) continue; + if (operation.type === "move") await rollbackMove(paths, operation); + else await rollbackWriteOrRemove(paths, current, operation, hooks); + } + await appendNativeTransactionEvent(paths, current.id, "rollback-completed"); + current = await setStatus(paths, current, "rolled-back"); + return current; +} +async function assertCommittedArchiveCasTarget(paths, journal, operation) { + const target = await resolveRef2(paths, operation.target); + if (operation.type === "write") { + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord) { + throw new Error(`Archive commit target ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: target, + operation, + record: postRecord, + label: `Archive commit target ${operation.target}` + }); + return; + } + await assertContent({ + target, + expectedHash: null, + label: `Archive commit target ${operation.target}` + }); +} +async function cleanupCommittedArchiveCas(paths, journal) { + for (const operation of journal.operations) { + if (operation.type === "move") continue; + const target = await resolveRef2(paths, operation.target); + const cas = await archiveCasPaths(paths, journal, operation, target); + if (await pathExists(cas.rollbackQuarantine)) { + throw new Error(`Archive commit found an unfinished rollback for ${operation.target}`); + } + await assertCommittedArchiveCasTarget(paths, journal, operation); + if (operation.expectedTargetHash !== null) { + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error(`Archive commit target ${operation.target} has no original CAS identity`); + } + if (await pathExists(cas.originalQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Archive commit original quarantine ${operation.target}` + }); + } + } + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + } + for (const operation of journal.operations) { + if (operation.type !== "move") { + await assertCommittedArchiveCasTarget(paths, journal, operation); + } + } +} +async function finalizeNativeArchiveTransactionV2(paths, journal, event) { + if (event === "commit") await cleanupCommittedArchiveCas(paths, journal); + await appendNativeTransactionEvent(paths, journal.id, event); + return event === "commit" ? setStatus(paths, journal, "committed") : journal; +} + +// domains/comet-native/native-checkpoint-journal.ts +import { randomUUID as randomUUID4 } from "crypto"; +import { promises as fs21 } from "fs"; + +// domains/comet-native/native-checkpoint-storage.ts +import path32 from "path"; +var NATIVE_CHECKPOINT_LIMITS = { + maxArtifacts: 128, + maxFileBytes: 16 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxDocumentBytes: 256 * 1024 +}; +var HASH_PATTERN13 = /^[a-f0-9]{64}$/u; +function record7(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactKeys3(value, keys, label) { + const expected = new Set(keys); + const unknown = Object.keys(value).filter((key) => !expected.has(key)); + const missing = keys.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function stringValue(value, label, max = 2e3) { + if (typeof value !== "string" || value.length === 0 || value.length > max) { + throw new Error(`${label} must be a non-empty string of at most ${max} characters`); + } + return value; +} +function positiveInteger6(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function nonNegativeInteger3(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function normalizeNativeCheckpointArtifactRef(value) { + const trimmed = value.trim().replaceAll("\\", "/"); + if (trimmed.length === 0 || path32.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|\/)/u.test(trimmed) || trimmed.split("/").includes("..")) { + throw new Error(`Checkpoint artifact must be project-relative: ${value}`); + } + const normalized = path32.posix.normalize(trimmed); + if (normalized === "." || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`Checkpoint artifact must name a project file: ${value}`); + } + return normalized; +} +function nativeProgressCheckpointFile(paths, name) { + return path32.join(nativeChangeDir(paths, name), "runtime", "checkpoints", "progress.json"); +} +function nativeCheckpointJournalFile(paths, name) { + return path32.join(nativeChangeDir(paths, name), "runtime", "checkpoint-journal.json"); +} +function nativeCheckpointManifestFile(paths, name, hash6) { + if (!HASH_PATTERN13.test(hash6)) throw new Error("Native checkpoint manifest hash is invalid"); + return path32.join( + nativeChangeDir(paths, name), + "runtime", + "checkpoints", + "manifests", + `${hash6}.json` + ); +} +function nativeCheckpointManifestRef(hash6) { + if (!HASH_PATTERN13.test(hash6)) throw new Error("Native checkpoint manifest hash is invalid"); + return `runtime/checkpoints/manifests/${hash6}.json`; +} +async function readBoundedJson(root, file, label) { + const snapshot2 = await readNativeProtectedFile({ + root, + file, + maxBytes: NATIVE_CHECKPOINT_LIMITS.maxDocumentBytes, + label + }); + return JSON.parse(snapshot2.bytes.toString("utf8")); +} +function parseArtifact(value, index) { + const artifact = record7(value, `checkpoint manifest artifact ${index}`); + exactKeys3(artifact, ["path", "hash", "size"], `checkpoint manifest artifact ${index}`); + const artifactPath = normalizeNativeCheckpointArtifactRef( + stringValue(artifact.path, `checkpoint artifact ${index} path`, 4096) + ); + const sensitiveReason = nativeSensitiveRelativePathReason(artifactPath); + if (sensitiveReason) { + throw new Error( + `checkpoint artifact ${index} is excluded as sensitive (${sensitiveReason}): ${artifactPath}` + ); + } + if (typeof artifact.hash !== "string" || !HASH_PATTERN13.test(artifact.hash)) { + throw new Error(`checkpoint artifact ${index} hash is invalid`); + } + return { + path: artifactPath, + hash: artifact.hash, + size: nonNegativeInteger3(artifact.size, `checkpoint artifact ${index} size`) + }; +} +function parseNativeCheckpointManifestValue(value, expectedName) { + const manifest = record7(value, "Native checkpoint manifest"); + exactKeys3( + manifest, + ["schema", "change", "artifacts", "totalBytes"], + "Native checkpoint manifest" + ); + if (manifest.schema !== "comet.native.checkpoint-manifest.v1") { + throw new Error("Native checkpoint manifest schema is invalid"); + } + if (manifest.change !== expectedName) + throw new Error("Native checkpoint manifest change mismatch"); + if (!Array.isArray(manifest.artifacts)) { + throw new Error("Native checkpoint manifest artifacts must be an array"); + } + if (manifest.artifacts.length > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error("Native checkpoint manifest has too many artifacts"); + } + const artifacts = manifest.artifacts.map(parseArtifact); + const sorted = [...artifacts].sort((left, right) => left.path.localeCompare(right.path)); + if (JSON.stringify(artifacts) !== JSON.stringify(sorted)) { + throw new Error("Native checkpoint manifest artifacts must be sorted"); + } + if (new Set(artifacts.map((artifact) => artifact.path)).size !== artifacts.length) { + throw new Error("Native checkpoint manifest has duplicate artifacts"); + } + const totalBytes = nonNegativeInteger3(manifest.totalBytes, "checkpoint manifest totalBytes"); + if (artifacts.reduce((total, artifact) => total + artifact.size, 0) !== totalBytes) { + throw new Error("Native checkpoint manifest totalBytes mismatch"); + } + if (totalBytes > NATIVE_CHECKPOINT_LIMITS.maxTotalBytes) { + throw new Error("Native checkpoint manifest totalBytes exceeds its budget"); + } + return { + schema: "comet.native.checkpoint-manifest.v1", + change: expectedName, + artifacts, + totalBytes + }; +} +function hashNativeCheckpointManifest(manifest) { + return sha256Text(JSON.stringify(parseNativeCheckpointManifestValue(manifest, manifest.change))); +} +function parseNativeProgressCheckpointValue(value, expectedName) { + const checkpoint = record7(value, "Native progress checkpoint"); + exactKeys3( + checkpoint, + [ + "schema", + "id", + "change", + "phase", + "previousRevision", + "stateRevision", + "summary", + "nextAction", + "inputHash", + "manifestHash", + "manifestRef", + "artifactCount", + "createdAt" + ], + "Native progress checkpoint" + ); + if (checkpoint.schema !== "comet.native.progress-checkpoint.v1") { + throw new Error("Native progress checkpoint schema is invalid"); + } + if (checkpoint.change !== expectedName) throw new Error("Native checkpoint change mismatch"); + const phase = checkpoint.phase; + if (phase !== "shape" && phase !== "build" && phase !== "verify" && phase !== "archive") { + throw new Error("Native checkpoint phase is invalid"); + } + const previousRevision = positiveInteger6( + checkpoint.previousRevision, + "Native checkpoint previousRevision" + ); + const stateRevision = positiveInteger6( + checkpoint.stateRevision, + "Native checkpoint stateRevision" + ); + if (stateRevision !== previousRevision + 1) { + throw new Error("Native checkpoint stateRevision must increment previousRevision once"); + } + const manifestHash = stringValue(checkpoint.manifestHash, "Native checkpoint manifestHash", 64); + if (!HASH_PATTERN13.test(manifestHash)) + throw new Error("Native checkpoint manifestHash is invalid"); + const expectedManifestRef = nativeCheckpointManifestRef(manifestHash); + if (checkpoint.manifestRef !== expectedManifestRef) { + throw new Error("Native checkpoint manifestRef does not match manifestHash"); + } + const inputHash = stringValue(checkpoint.inputHash, "Native checkpoint inputHash", 64); + if (!HASH_PATTERN13.test(inputHash)) throw new Error("Native checkpoint inputHash is invalid"); + const createdAt = stringValue(checkpoint.createdAt, "Native checkpoint createdAt", 64); + if (Number.isNaN(Date.parse(createdAt))) + throw new Error("Native checkpoint createdAt is invalid"); + const artifactCount = nonNegativeInteger3( + checkpoint.artifactCount, + "Native checkpoint artifactCount" + ); + if (artifactCount > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error("Native checkpoint artifactCount exceeds its budget"); + } + const summary = stringValue(checkpoint.summary, "Native checkpoint summary"); + const nextAction = stringValue(checkpoint.nextAction, "Native checkpoint nextAction"); + if (redactNativeCredentialText(summary) !== summary || redactNativeCredentialText(nextAction) !== nextAction) { + throw new Error("Native checkpoint text contains unredacted credential material"); + } + return { + schema: "comet.native.progress-checkpoint.v1", + id: stringValue(checkpoint.id, "Native checkpoint id", 128), + change: expectedName, + phase, + previousRevision, + stateRevision, + summary, + nextAction, + inputHash, + manifestHash, + manifestRef: expectedManifestRef, + artifactCount, + createdAt + }; +} +function parseNativeCheckpointJournalValue(value, expectedName) { + const journal = record7(value, "Native checkpoint journal"); + exactKeys3( + journal, + [ + "schema", + "id", + "change", + "inputHash", + "createdAt", + "previousState", + "nextState", + "checkpoint", + "manifest" + ], + "Native checkpoint journal" + ); + if (journal.schema !== "comet.native.checkpoint-journal.v1") { + throw new Error("Native checkpoint journal schema is invalid"); + } + if (journal.change !== expectedName) throw new Error("Native checkpoint journal change mismatch"); + const previousState = parseNativeChangeValue(journal.previousState); + const nextState = parseNativeChangeValue(journal.nextState); + const checkpoint = parseNativeProgressCheckpointValue(journal.checkpoint, expectedName); + const manifest = parseNativeCheckpointManifestValue(journal.manifest, expectedName); + const inputHash = stringValue(journal.inputHash, "Native checkpoint journal inputHash", 64); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: checkpoint.summary, + nextAction: checkpoint.nextAction, + artifacts: manifest.artifacts + }) + ); + if (!HASH_PATTERN13.test(inputHash) || inputHash !== checkpoint.inputHash || inputHash !== expectedInputHash || journal.id !== checkpoint.id || journal.createdAt !== checkpoint.createdAt) { + throw new Error("Native checkpoint journal envelope mismatch"); + } + if (previousState.name !== expectedName || nextState.name !== expectedName || nextState.revision !== previousState.revision + 1 || checkpoint.previousRevision !== previousState.revision || checkpoint.stateRevision !== nextState.revision || checkpoint.phase !== nextState.phase || checkpoint.manifestHash !== hashNativeCheckpointManifest(manifest) || checkpoint.artifactCount !== manifest.artifacts.length) { + throw new Error("Native checkpoint journal state mismatch"); + } + return { + schema: "comet.native.checkpoint-journal.v1", + id: checkpoint.id, + change: expectedName, + inputHash, + createdAt: checkpoint.createdAt, + previousState, + nextState, + checkpoint, + manifest + }; +} +async function hashProjectArtifact(paths, artifactRef, hooks) { + const target = path32.resolve(paths.projectRoot, ...artifactRef.split("/")); + if (!isInsidePath(paths.projectRoot, target) || isInsidePath(paths.nativeRoot, target)) { + throw new Error(`Checkpoint artifact is outside project content: ${artifactRef}`); + } + const sensitiveReason = nativeSensitiveArtifactReason(paths, artifactRef); + if (sensitiveReason) { + throw new Error( + `Checkpoint artifact is excluded as sensitive (${sensitiveReason}): ${artifactRef}` + ); + } + const snapshot2 = await readNativeProtectedFile({ + root: paths.projectRoot, + file: target, + maxBytes: NATIVE_CHECKPOINT_LIMITS.maxFileBytes, + label: `Checkpoint artifact ${artifactRef}`, + forbiddenRoots: [paths.nativeRoot], + hooks: { + afterParentChainCaptured: () => hooks?.afterParentChainCaptured?.(artifactRef), + afterOpen: () => hooks?.afterOpen?.(artifactRef), + beforeRead: () => hooks?.beforeRead?.(artifactRef) + } + }); + return { path: artifactRef, hash: snapshot2.hash, size: snapshot2.size }; +} +async function createNativeCheckpointManifest(paths, name, artifactRefs, hooks) { + const normalized = artifactRefs.map(normalizeNativeCheckpointArtifactRef).sort(); + if (normalized.length > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error( + `Checkpoint supports at most ${NATIVE_CHECKPOINT_LIMITS.maxArtifacts} artifacts` + ); + } + if (new Set(normalized).size !== normalized.length) { + throw new Error("Checkpoint artifacts must not contain duplicates"); + } + const artifacts = []; + let totalBytes = 0; + for (const artifactRef of normalized) { + const artifact = await hashProjectArtifact(paths, artifactRef, hooks); + totalBytes += artifact.size; + if (totalBytes > NATIVE_CHECKPOINT_LIMITS.maxTotalBytes) { + throw new Error("Checkpoint artifacts exceed the total byte budget"); + } + artifacts.push(artifact); + } + return { + schema: "comet.native.checkpoint-manifest.v1", + change: name, + artifacts, + totalBytes + }; +} +async function readNativeProgressCheckpoint(paths, name) { + const file = nativeProgressCheckpointFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + return parseNativeProgressCheckpointValue( + await readBoundedJson(paths.nativeRoot, file, "Native progress checkpoint"), + name + ); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function readNativeCheckpointManifest(paths, name, hash6) { + const file = nativeCheckpointManifestFile(paths, name, hash6); + await resolveContainedNativePath(paths.nativeRoot, file); + const value = await readBoundedJson(paths.nativeRoot, file, "Native checkpoint manifest"); + const manifest = parseNativeCheckpointManifestValue(value, name); + assertCheckpointManifestSafeForPaths(paths, manifest); + if (hashNativeCheckpointManifest(manifest) !== hash6) { + throw new Error("Native checkpoint manifest content hash mismatch"); + } + return manifest; +} +async function readNativeCheckpointJournal(paths, name) { + const file = nativeCheckpointJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const journal = parseNativeCheckpointJournalValue( + await readBoundedJson(paths.nativeRoot, file, "Native checkpoint journal"), + name + ); + assertCheckpointManifestSafeForPaths(paths, journal.manifest); + return journal; + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function writeNativeCheckpointManifest(paths, name, manifest, hooks) { + const parsed = parseNativeCheckpointManifestValue(manifest, name); + assertCheckpointManifestSafeForPaths(paths, parsed); + const hash6 = hashNativeCheckpointManifest(parsed); + const file = nativeCheckpointManifestFile(paths, name, hash6); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const existing = await readNativeCheckpointManifest(paths, name, hash6); + if (JSON.stringify(existing) !== JSON.stringify(parsed)) { + throw new Error("Native checkpoint manifest hash collision"); + } + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, parsed, { + containedRoot: paths.nativeRoot, + beforeCommit: hooks?.beforeCommit + }); + return hash6; +} +function assertCheckpointManifestSafeForPaths(paths, manifest) { + for (const artifact of manifest.artifacts) { + const reason = nativeSensitiveArtifactReason(paths, artifact.path); + if (reason) { + throw new Error( + `Native checkpoint manifest contains a sensitive artifact (${reason}): ${artifact.path}` + ); + } + } +} +async function writeNativeProgressCheckpoint(paths, checkpoint) { + const parsed = parseNativeProgressCheckpointValue(checkpoint, checkpoint.change); + const manifest = await readNativeCheckpointManifest(paths, parsed.change, parsed.manifestHash); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: parsed.summary, + nextAction: parsed.nextAction, + artifacts: manifest.artifacts + }) + ); + if (parsed.inputHash !== expectedInputHash || parsed.artifactCount !== manifest.artifacts.length) { + throw new Error("Native progress checkpoint does not match its artifact manifest"); + } + const file = nativeProgressCheckpointFile(paths, checkpoint.change); + await resolveContainedNativePath(paths.nativeRoot, file); + await atomicWriteJson(file, parsed, { containedRoot: paths.nativeRoot }); +} +async function writeNativeCheckpointJournal(paths, journal) { + const parsed = parseNativeCheckpointJournalValue(journal, journal.change); + const file = nativeCheckpointJournalFile(paths, journal.change); + await resolveContainedNativePath(paths.nativeRoot, file); + if (await readNativeCheckpointJournal(paths, journal.change)) { + throw new Error(`Native checkpoint recovery is already pending for ${journal.change}`); + } + await atomicWriteJson(file, parsed, { containedRoot: paths.nativeRoot }); +} +async function inspectNativeCheckpointFreshness(options) { + let checkpoint; + try { + checkpoint = await readNativeProgressCheckpoint(options.paths, options.name); + } catch (error) { + return { + checkpoint: null, + manifest: null, + freshness: "stale", + reasons: ["checkpoint-progress-invalid"], + findings: [ + { + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint is invalid: ${error.message}. Automatic repair is unavailable; inspect and move the invalid checkpoint file aside before retrying`, + path: nativeProgressCheckpointFile(options.paths, options.name) + } + ] + }; + } + if (!checkpoint) { + return { + checkpoint: null, + manifest: null, + freshness: "fresh", + reasons: ["no-checkpoint"], + findings: [] + }; + } + const reasons = []; + const findings = []; + if (checkpoint.stateRevision !== options.stateRevision) reasons.push("state-revision-changed"); + let manifest = null; + try { + manifest = await readNativeCheckpointManifest( + options.paths, + options.name, + checkpoint.manifestHash + ); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: checkpoint.summary, + nextAction: checkpoint.nextAction, + artifacts: manifest.artifacts + }) + ); + if (checkpoint.inputHash !== expectedInputHash || checkpoint.artifactCount !== manifest.artifacts.length) { + throw new Error("Native progress checkpoint does not match its artifact manifest"); + } + for (const expected of manifest.artifacts) { + try { + const actual = await hashProjectArtifact(options.paths, expected.path); + if (actual.hash !== expected.hash || actual.size !== expected.size) { + reasons.push(`artifact-changed:${expected.path}`); + } + } catch { + reasons.push(`artifact-unavailable:${expected.path}`); + } + } + } catch (error) { + reasons.push("checkpoint-manifest-invalid"); + findings.push({ + code: "checkpoint-manifest-invalid", + message: `Native checkpoint manifest is invalid: ${error.message}`, + path: nativeCheckpointManifestFile(options.paths, options.name, checkpoint.manifestHash) + }); + } + return { + checkpoint, + manifest, + freshness: reasons.length === 0 ? "fresh" : "stale", + reasons, + findings + }; +} + +// domains/comet-native/native-checkpoint-journal.ts +async function prepareNativeCheckpointJournal(options) { + const createdAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString(); + const id = options.checkpointId?.() ?? randomUUID4(); + const checkpoint = { + ...options.checkpoint, + id, + createdAt + }; + const journal = { + schema: "comet.native.checkpoint-journal.v1", + id, + change: options.previousState.name, + inputHash: checkpoint.inputHash, + createdAt, + previousState: options.previousState, + nextState: options.nextState, + checkpoint, + manifest: options.manifest + }; + await writeNativeCheckpointManifest(options.paths, options.previousState.name, options.manifest); + await writeNativeCheckpointJournal(options.paths, journal); + return journal; +} +async function continueNativeCheckpointLocked(paths, name, hooks) { + const journal = await readNativeCheckpointJournal(paths, name); + if (!journal) return null; + const manifestHash = await writeNativeCheckpointManifest(paths, journal.change, journal.manifest); + if (manifestHash !== journal.checkpoint.manifestHash) { + throw new Error("Native checkpoint recovery manifest hash mismatch"); + } + await compareAndSwapNativeChangeLocked(paths, journal.nextState, journal.previousState.revision, { + allowPendingCheckpointRecovery: true + }); + await hooks?.afterStateWritten?.(journal); + await writeNativeProgressCheckpoint(paths, journal.checkpoint); + await hooks?.afterProgressWritten?.(journal); + await fs21.rm(nativeCheckpointJournalFile(paths, name), { force: true }); + return journal; +} +async function continueNativeCheckpoint(paths, name, hooks) { + return withNativeMutationLock( + paths, + `continue checkpoint ${name}`, + () => withNativeTransitionLock(paths, name, `continue checkpoint ${name}`, async () => { + await continueNativeTransitionLocked(paths, name); + return continueNativeCheckpointLocked(paths, name, hooks); + }) + ); +} + +// domains/comet-native/native-change-recovery.ts +async function settleNativeChangeJournalsLocked(paths, name) { + await continueNativeTransitionLocked(paths, name); + await continueNativeCheckpointLocked(paths, name); +} + +// domains/comet-entry/current-selection.ts +import { randomUUID as randomUUID5 } from "crypto"; +import { promises as fs22 } from "fs"; +import path33 from "path"; +var COMET_CURRENT_SELECTION_SCHEMA = "comet.selection.v2"; +var COMET_CURRENT_SELECTION_MAX_BYTES = 16 * 1024; +function cometCurrentSelectionFile(projectRoot) { + return path33.join(projectRoot, ".comet", "current-change.json"); +} +function isRecord(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function validBranch(value) { + return value === null || typeof value === "string"; +} +function parseSelection(source) { + let value; + try { + value = JSON.parse(source); + } catch (error) { + throw new Error( + `current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + if (!isRecord(value)) { + throw new Error("current change selection must be a JSON object"); + } + if (value.version === 1) { + if (typeof value.change !== "string") { + throw new Error("legacy current change selection change must be a string"); + } + if (value.branch !== void 0 && !validBranch(value.branch)) { + throw new Error("legacy current change selection branch must be a string or null"); + } + return { + selection: { + schema: COMET_CURRENT_SELECTION_SCHEMA, + workflow: "classic", + change: value.change, + branch: value.branch ?? null + }, + legacy: true + }; + } + if (value.schema !== COMET_CURRENT_SELECTION_SCHEMA) { + throw new Error(`current change selection schema must be ${COMET_CURRENT_SELECTION_SCHEMA}`); + } + if (value.workflow !== "native" && value.workflow !== "classic") { + throw new Error("current change selection workflow must be native or classic"); + } + if (typeof value.change !== "string") { + throw new Error("current change selection change must be a string"); + } + if (!validBranch(value.branch)) { + throw new Error("current change selection branch must be a string or null"); + } + if (value.workflow === "native" && value.branch !== null) { + throw new Error("Native current change selection branch must be null"); + } + return { selection: value, legacy: false }; +} +async function readCometCurrentSelection(projectRoot) { + let source; + try { + const file = cometCurrentSelectionFile(projectRoot); + const { bytes } = await readFileRaceSafe(file, COMET_CURRENT_SELECTION_MAX_BYTES, { + label: "current change selection" + }); + source = bytes.toString("utf8"); + } catch (error) { + if (error.code === "ENOENT") return { status: "missing" }; + throw new Error( + `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + const parsed = parseSelection(source); + return { status: "selected", ...parsed }; +} +async function writeCometCurrentSelection(projectRoot, selection) { + const parsed = parseSelection(JSON.stringify(selection)); + if (parsed.legacy) throw new Error("cannot write a legacy current change selection"); + const file = cometCurrentSelectionFile(projectRoot); + const temporary = `${file}.${randomUUID5()}.tmp`; + await fs22.mkdir(path33.dirname(file), { recursive: true }); + try { + await fs22.writeFile(temporary, `${JSON.stringify(parsed.selection, null, 2)} +`, "utf8"); + await fs22.rename(temporary, file); + } catch (error) { + await fs22.rm(temporary, { force: true }); + throw error; + } +} +async function clearCometCurrentSelection(projectRoot) { + await fs22.rm(cometCurrentSelectionFile(projectRoot), { force: true }); +} +async function clearCometCurrentSelectionIf(projectRoot, workflow, change) { + const current = await readCometCurrentSelection(projectRoot); + if (current.status !== "selected" || current.selection.workflow !== workflow || current.selection.change !== change) { + return false; + } + await clearCometCurrentSelection(projectRoot); + return true; +} + +// domains/comet-native/native-selection.ts +var NATIVE_SELECTION_MAX_BYTES = 16 * 1024; +async function readNativeSelectionRecord(paths) { + const current = await readCometCurrentSelection(paths.projectRoot); + if (current.status === "missing" || current.selection.workflow !== "native") return null; + assertNativeName(current.selection.change); + return current.selection; +} +function nativeSelectionFile(paths) { + return cometCurrentSelectionFile(paths.projectRoot); +} +async function selectNativeChange(paths, name) { + return withNativeMutationLock(paths, `select change ${name}`, async () => { + assertNativeName(name); + await readNativeChange(paths, name); + await writeCometCurrentSelection(paths.projectRoot, { + schema: "comet.selection.v2", + workflow: "native", + change: name, + branch: null + }); + }); +} +async function resolveSelectedNativeChange(paths) { + const value = await readNativeSelectionRecord(paths); + if (!value) return null; + await readNativeChange(paths, value.change); + return value.change; +} +async function clearNativeSelectionIfLocked(paths, name) { + await assertNoPendingNativeRootMove(paths.projectRoot); + return clearCometCurrentSelectionIf(paths.projectRoot, "native", name); +} + +// domains/comet-native/native-archive.ts +var NATIVE_ARCHIVE_COPY_MAX_BYTES2 = 16 * 1024 * 1024; +var NativeSpecConflictError = class extends Error { + constructor(capability, expectedHash, actualHash, canonicalPath) { + super( + `Canonical spec conflict for ${capability}: expected ${expectedHash ?? "(missing)"}, actual ${actualHash ?? "(missing)"}` + ); + this.capability = capability; + this.expectedHash = expectedHash; + this.actualHash = actualHash; + this.canonicalPath = canonicalPath; + this.name = "NativeSpecConflictError"; + } + capability; + expectedHash; + actualHash; + canonicalPath; + code = "native-spec-conflict"; +}; +var NativeArchivePreflightError = class extends Error { + constructor(preflight, message = preflight.ready ? "Native Archive preflight no longer matches the expected hash" : `Native Archive preflight is blocked: ${preflight.findingCodes.join(", ")}`) { + super(message); + this.preflight = preflight; + this.name = "NativeArchivePreflightError"; + } + preflight; + code = "native-archive-preflight"; +}; +async function optionalHash2(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +function assertArchiveReady(state) { + if (state.phase !== "archive") throw new Error(`Native change ${state.name} is not in Archive`); + if (state.verification_result !== "pass") { + throw new Error(`Native change ${state.name} has not passed verification`); + } + if (!state.verification_report) { + throw new Error(`Native change ${state.name} has no verification report`); + } + if (state.archived) throw new Error(`Native change ${state.name} is already archived`); +} +async function assertArchiveArtifacts(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const brief = await validateNativeBrief(changeDir, state.brief); + const verification = await validateNativeVerification(changeDir, state.verification_report); + const findings = [...brief.findings, ...verification.findings]; + if (findings.length > 0) { + throw new Error(`Native archive artifacts are invalid: ${findings[0].message}`); + } +} +async function assertSpecBase(paths, change) { + const canonical = canonicalSpecPath(paths, change.capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const actual = await optionalHash2(canonical); + const expected = change.operation === "create" ? null : change.base_hash; + if (actual !== expected) { + throw new NativeSpecConflictError(change.capability, expected, actual, canonical); + } +} +function archiveTarget(paths, name, now) { + return path34.join(paths.archiveDir, `${now.toISOString().slice(0, 10)}-${name}`); +} +async function pathExists2(target) { + try { + await fs23.access(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function buildArchiveJournal(options) { + const { paths, state, now, transactionId, preflight } = options; + const target = archiveTarget(paths, state.name, now); + if (await pathExists2(target)) throw new Error(`Native archive target already exists: ${target}`); + if (nativeRootRef(paths, target) !== preflight.targetRef) { + throw new Error("Native Archive target changed after preflight"); + } + const tx = await resolveNativeTransactionPaths(paths, transactionId); + const operations = []; + for (const [index, change] of state.spec_changes.entries()) { + await assertSpecBase(paths, change); + const canonical = canonicalSpecPath(paths, change.capability); + const preview = preflight.operations.find( + (operation) => operation.capability === change.capability && operation.operation === change.operation + ); + if (!preview) { + throw new Error(`Native Archive preflight has no operation for ${change.capability}`); + } + if (change.operation !== "remove" && preview.proposedHash === null) { + throw new Error(`Native Archive preflight has no proposed hash for ${change.capability}`); + } + const backup = path34.join(tx.backups, "specs", change.capability, "spec.md"); + if (change.operation === "remove") { + operations.push({ + id: `spec-${index + 1}-${change.capability}`, + type: "remove", + target: nativeRootRef(paths, canonical), + backup: nativeRootRef(paths, backup), + expectedTargetHash: change.base_hash + }); + continue; + } + const changeDir = nativeChangeDir(paths, state.name); + await resolveNativeArtifactFile(changeDir, change.source); + const source2 = path34.resolve(changeDir, ...change.source.split(/[\\/]/u)); + const staged = path34.join(tx.staged, "specs", change.capability, "spec.md"); + const stagedSnapshot = await copyNativeProtectedFile({ + sourceRoot: changeDir, + source: source2, + targetRoot: paths.nativeRoot, + target: staged, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES2, + label: `Native Archive proposed spec ${change.capability}`, + expectedHash: preview.proposedHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => options.hooks?.afterProtectedCopySourceParentCaptured?.("stage", change.source) + } + }); + const stagedHash = stagedSnapshot.hash; + if (stagedHash !== preview.proposedHash) { + throw new Error(`Proposed Native spec changed after preflight: ${change.capability}`); + } + operations.push({ + id: `spec-${index + 1}-${change.capability}`, + type: "write", + target: nativeRootRef(paths, canonical), + staged: nativeRootRef(paths, staged), + ...change.operation === "replace" ? { backup: nativeRootRef(paths, backup) } : {}, + expectedTargetHash: change.operation === "create" ? null : change.base_hash, + stagedHash + }); + } + const source = nativeChangeDir(paths, state.name); + operations.push({ + id: "archive-change", + type: "move", + source: nativeRootRef(paths, source), + target: nativeRootRef(paths, target), + expectedSourceHash: await hashNativeArchiveTree(source), + expectedTargetHash: null + }); + return { + schema: "comet.native.transaction.v2", + id: transactionId, + kind: "archive", + status: "prepared", + change: state.name, + createdAt: now.toISOString(), + preflightHash: preflight.preflightHash, + operations + }; +} +function archiveDirectoryFromJournal(paths, journal) { + const operation = journal.operations.find((item) => item.id === "archive-change"); + if (!operation || operation.type !== "move") { + throw new Error(`Archive transaction ${journal.id} has no archive move`); + } + return path34.resolve(paths.nativeRoot, ...operation.target.split("/")); +} +async function finalizeArchive(paths, journal, hooks) { + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some((event2) => event2.type === "archive-finalized")) return; + const finalizationStarted = events.some((event2) => event2.type === "archive-finalization-started"); + if (journal.schema === "comet.native.transaction.v2" && !finalizationStarted) { + const move = journal.operations.find((operation) => operation.id === "archive-change"); + if (!move || move.type !== "move" || !move.expectedSourceHash) { + throw new Error(`Archive transaction ${journal.id} has no content-bound archive move`); + } + const archiveContent = await inspectNativeArchiveContent( + archiveDirectoryFromJournal(paths, journal) + ); + if (archiveContent?.kind !== "directory" || archiveContent.hash !== move.expectedSourceHash) { + throw new Error( + `Native Archive content changed before finalization for transaction ${journal.id}` + ); + } + } + const archiveDir = archiveDirectoryFromJournal(paths, journal); + const stateFile = path34.join(archiveDir, NATIVE_CHANGE_STATE_FILE); + const state = await readNativeChangeFile(stateFile); + if (!journal.change || state.name !== journal.change) { + throw new Error(`Archive transaction ${journal.id} change mismatch`); + } + const run = await readNativeRunState(archiveDir); + if (!run || run.runId !== state.run_id || run.currentStep !== "archive" && !(run.currentStep === null && run.status === "completed")) { + throw new Error(`Native archive Run state is missing or inconsistent for ${state.name}`); + } + let completed = run; + if (run.currentStep === "archive") { + const decision = decideWithResolver( + NATIVE_RUNTIME_PACKAGE, + run, + /* @__PURE__ */ new Set(), + nativePhaseResolver, + void 0 + ); + if (!decision.action) throw new Error(decision.reason ?? "Native archive produced no action"); + completed = recordOutcomeWithResolver( + NATIVE_RUNTIME_PACKAGE, + decision.state, + { + actionId: decision.action.id, + status: "succeeded", + summary: `Archived Native change ${state.name}` + }, + nativePhaseResolver, + void 0 + ); + } + const evidenceHash = sha256Text(`archive:${journal.id}:${state.name}`); + const trajectory = await readNativeTrajectory(archiveDir, completed.trajectoryRef); + const transactionEvents = trajectory.filter((item) => item.data.transactionId === journal.id); + if (journal.schema === "comet.native.transaction.v2" && (transactionEvents.length > 1 || transactionEvents.some((item) => item.type !== "state_transitioned"))) { + throw new Error(`Native Archive trajectory has a transaction id collision: ${journal.id}`); + } + let event = transactionEvents.find((item) => item.type === "state_transitioned"); + const eventData = { + previousPhase: "archive", + nextPhase: null, + evidenceHash, + summary: `Archived Native change ${state.name}`, + transactionId: journal.id + }; + if (event && journal.schema === "comet.native.transaction.v2" && (!isDeepStrictEqual2(event.data, eventData) || event.runId !== completed.runId || event.timestamp !== journal.createdAt || event !== trajectory.at(-1))) { + throw new Error(`Native Archive trajectory event changed for transaction ${journal.id}`); + } + if (!finalizationStarted && (state.archived || run.currentStep === null || transactionEvents.length > 0)) { + throw new Error( + `Native Archive finalization state changed before its irreversible marker: ${journal.id}` + ); + } + if (!finalizationStarted) { + if (journal.schema === "comet.native.transaction.v2") { + await finalizeNativeArchiveTransactionV2(paths, journal, "archive-finalization-started"); + await hooks?.afterFinalizationStarted?.(journal); + } else { + await finalizeNativeTransaction(paths, journal, "archive-finalization-started"); + } + } + if (!state.archived) { + const updated = { ...state, archived: true }; + await writeNativeChangeFile(stateFile, updated); + } + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir: archiveDir, + run: completed, + type: "state_transitioned", + data: eventData, + ...journal.schema === "comet.native.transaction.v2" ? { now: new Date(journal.createdAt) } : {} + }); + } + await writeNativeCheckpoint2({ + changeDir: archiveDir, + run: completed, + trajectoryOffset: event.sequence, + evidenceHash, + ...journal.schema === "comet.native.transaction.v2" ? { now: new Date(journal.createdAt) } : {} + }); + await writeNativeRunState(archiveDir, completed); + await clearNativeSelectionIfLocked(paths, state.name); + if (journal.schema === "comet.native.transaction.v2") { + await finalizeNativeArchiveTransactionV2(paths, journal, "archive-finalized"); + } else { + await finalizeNativeTransaction(paths, journal, "archive-finalized"); + } +} +async function continueArchive(paths, journal, hooks) { + if (journal.schema === "comet.native.transaction.v2") { + const events = await readNativeTransactionEvents(paths, journal.id); + if (!events.some((event) => event.type === "operation-started")) { + const preflight = await inspectNativeArchivePreflight({ + paths, + name: journal.change, + now: new Date(journal.createdAt) + }); + if (!preflight.ready || preflight.preflightHash !== journal.preflightHash) { + throw new NativeArchivePreflightError( + preflight, + "Native Archive facts changed before the first transaction operation" + ); + } + } + const applied2 = await applyNativeArchiveTransactionV2(paths, journal, hooks); + await finalizeArchive(paths, applied2, hooks); + return finalizeNativeArchiveTransactionV2(paths, applied2, "commit"); + } + const applied = await applyNativeTransaction(paths, journal); + await finalizeArchive(paths, applied, hooks); + return finalizeNativeTransaction(paths, applied, "commit"); +} +function assertMatchingJournal(paths, journal) { + if (journal.kind !== "archive") throw new Error(`Transaction ${journal.id} is not an archive`); + if (journal.schema === "comet.native.transaction.v2") { + if (!journal.change) throw new Error(`Archive transaction ${journal.id} has no change`); + return; + } + if (path34.resolve(journal.projectRoot) !== path34.resolve(paths.projectRoot) || path34.resolve(journal.nativeRoot) !== path34.resolve(paths.nativeRoot)) { + throw new Error(`Transaction ${journal.id} belongs to a different Native root`); + } +} +async function archiveNativeChange(options) { + return withNativeMutationLock( + options.paths, + `archive ${options.name}`, + () => withNativeTransitionLock(options.paths, options.name, `archive ${options.name}`, async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const lock = await acquireNativeLock(options.paths, "archive", `archive ${options.name}`); + try { + if (!/^[a-f0-9]{64}$/u.test(options.expectedPreflightHash)) { + throw new Error("Native Archive expected preflight must be a SHA-256 hash"); + } + const now = options.now ?? /* @__PURE__ */ new Date(); + const preflight = await inspectNativeArchivePreflight({ + paths: options.paths, + name: options.name, + now + }); + if (!preflight.ready || preflight.preflightHash !== options.expectedPreflightHash) { + throw new NativeArchivePreflightError(preflight); + } + const state = await readNativeChange(options.paths, options.name); + assertArchiveReady(state); + await assertArchiveArtifacts(options.paths, state); + const transactionId = randomUUID6(); + const journal = await buildArchiveJournal({ + paths: options.paths, + state, + now, + transactionId, + preflight, + hooks: options.hooks + }); + await createNativeArchiveTransactionV2(options.paths, journal); + await options.hooks?.afterPrepared?.(journal); + await continueArchive(options.paths, journal, options.hooks); + return { + archiveDir: archiveDirectoryFromJournal(options.paths, journal), + transactionId, + preflightHash: preflight.preflightHash + }; + } finally { + await releaseNativeLock(lock); + } + }) + ); +} +async function recoverArchiveTransaction(options) { + return withNativeMutationLock( + options.paths, + `recover archive ${options.transactionId}`, + async () => { + const lock = await acquireNativeLock( + options.paths, + "archive", + `recover archive ${options.transactionId}` + ); + try { + const generic = await readNativeTransaction(options.paths, options.transactionId); + const journal = generic.schema === "comet.native.transaction.v2" ? await readNativeArchiveTransactionV2(options.paths, options.transactionId) : generic; + assertMatchingJournal(options.paths, journal); + if (journal.status === "committed" || journal.status === "rolled-back") return journal; + return options.strategy === "continue" ? continueArchive(options.paths, journal) : journal.schema === "comet.native.transaction.v2" ? rollbackNativeArchiveTransactionV2(options.paths, journal) : rollbackNativeTransaction(options.paths, journal); + } finally { + await releaseNativeLock(lock); + } + }, + { allowedTransactionId: options.transactionId } + ); +} + +// domains/comet-native/native-diagnostics.ts +import { promises as fs24 } from "fs"; + +// domains/comet-native/native-run-consistency.ts +import path35 from "path"; +function runPath(changeDir, ref) { + return path35.resolve(changeDir, ...ref.split(/[\\/]/u)); +} +async function inspectNativeRunConsistency(paths, state) { + const findings = []; + const changeDir = nativeChangeDir(paths, state.name); + const stateFile = runPath(changeDir, NATIVE_RUN_STORAGE.stateRef); + let run; + try { + run = await readNativeRunState(changeDir); + } catch (error) { + return [ + { + code: "run-state-invalid", + message: `Native Run state is invalid: ${error.message}`, + path: stateFile + } + ]; + } + if (!run) { + if (state.run_id !== null || state.phase !== "shape") { + findings.push({ + code: "run-state-missing", + message: "Native change references a missing Run state", + path: stateFile + }); + } + return findings; + } + if (state.run_id === null) { + return [ + { + code: "run-state-unexpected", + message: "Native change has a Run state but no run_id", + path: stateFile + } + ]; + } + if (run.runId !== state.run_id) { + findings.push({ + code: "run-id-mismatch", + message: `Native Run id ${run.runId} does not match change run_id ${state.run_id}`, + path: stateFile + }); + } + if (run.pending || run.status === "waiting") { + findings.push({ + code: "run-action-pending", + message: "Native Run has an unresolved pending action", + path: stateFile + }); + } + if (run.currentStep !== state.phase) { + findings.push({ + code: "run-phase-mismatch", + message: `Native Run step ${run.currentStep ?? "(none)"} does not match phase ${state.phase}`, + path: stateFile + }); + } + const trajectoryFile2 = runPath(changeDir, run.trajectoryRef); + const tailInspection = await inspectNativeTrajectoryTail(paths, state.name); + if (tailInspection.status === "repairable") { + findings.push({ + code: "trajectory-tail-incomplete", + message: `Native trajectory final line is incomplete at line ${tailInspection.line}; doctor repair can discard ${tailInspection.discardedBytes} incomplete byte(s)`, + path: trajectoryFile2 + }); + return findings; + } + if (tailInspection.status === "invalid") { + findings.push({ + code: "trajectory-invalid", + message: `Native trajectory is invalid at line ${tailInspection.line}: ${tailInspection.message}`, + path: trajectoryFile2 + }); + return findings; + } + let trajectory; + try { + trajectory = await readNativeTrajectory(changeDir, run.trajectoryRef); + if (trajectory.length === 0 || trajectory.some( + (event, index) => !event || typeof event !== "object" || event.sequence !== index + 1 || event.runId !== run.runId || typeof event.type !== "string" || !event.data || typeof event.data !== "object" || Array.isArray(event.data) + )) { + throw new Error("trajectory events are missing or inconsistent"); + } + } catch (error) { + findings.push({ + code: "trajectory-invalid", + message: `Native trajectory is invalid: ${error.message}`, + path: trajectoryFile2 + }); + return findings; + } + const checkpointFile = runPath(changeDir, run.checkpointRef); + try { + const checkpoint = await readNativeCheckpoint(changeDir, run.checkpointRef); + if (!checkpoint) { + findings.push({ + code: "checkpoint-missing", + message: "Native Run checkpoint is missing", + path: checkpointFile + }); + } else if (checkpoint.runId !== run.runId || checkpoint.stateVersion !== run.iteration || checkpoint.trajectoryOffset !== trajectory.length) { + findings.push({ + code: "checkpoint-mismatch", + message: "Native Run checkpoint does not match Run state and trajectory", + path: checkpointFile + }); + } + } catch (error) { + findings.push({ + code: "checkpoint-invalid", + message: `Native Run checkpoint is invalid: ${error.message}`, + path: checkpointFile + }); + } + return findings; +} + +// domains/comet-native/native-continuation.ts +var REPAIR_CODES = /^(?:run-|trajectory-|checkpoint-(?:missing|mismatch|invalid|progress-invalid)|transition-(?:incomplete|invalid))/u; +function requiredPhaseInputs(state) { + if (state.phase === "shape") { + return ["summary", "shared-understanding-confirmation"]; + } + if (state.phase === "build") { + return state.approval === "confirmed" ? ["summary", "artifact-or-no-code-reason"] : ["summary", "artifact-or-no-code-reason", "shared-understanding-confirmation"]; + } + if (state.phase === "verify") return ["summary", "verification-result", "verification-report"]; + return []; +} +function nativeContinuation(options) { + const findings = options.findings ?? []; + const actionableFindings = findings.filter( + (finding) => !isNativeWorkspaceAdvisoryCode(finding.code) + ); + const decision = actionableFindings.find((finding) => finding.requiresUserDecision); + const repair = actionableFindings.find( + (finding) => finding.repairCommand !== null || REPAIR_CODES.test(finding.code) + ); + const stagnationStop = actionableFindings.find( + (finding) => finding.code === "repair-stagnation-stop" + ); + const requiredInputs = [ + ...new Set(actionableFindings.map((finding) => finding.requiredAction)) + ].sort(); + if (options.done) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "done", + action: "none", + command: null, + requiresUserDecision: false, + requiredInputs: [] + }; + } + if (decision) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "await-user", + action: "work-phase", + command: null, + requiresUserDecision: true, + requiredInputs + }; + } + if (stagnationStop) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "work-phase", + command: null, + requiresUserDecision: false, + requiredInputs: ["implementation-progress-or-repair-override"] + }; + } + if (repair) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "repair", + command: repair.repairCommand, + requiresUserDecision: false, + requiredInputs + }; + } + if (options.evidenceRetreat) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "advance-phase", + command: `comet native next ${options.state.name} --summary ""`, + requiresUserDecision: false, + requiredInputs: ["summary"] + }; + } + if (actionableFindings.length > 0) { + if (options.state.phase === "archive") { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "none", + command: null, + requiresUserDecision: false, + requiredInputs + }; + } + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "work-phase", + command: null, + requiresUserDecision: false, + requiredInputs + }; + } + if (options.state.phase === "archive") { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: options.archiveReady ? "continue" : "blocked", + action: options.archiveReady ? "archive" : "none", + command: options.archiveReady ? `comet native archive ${options.state.name} --dry-run` : null, + requiresUserDecision: false, + requiredInputs: options.archiveReady ? [] : ["archive-readiness"] + }; + } + const confirmationSuffix = options.state.phase === "shape" || options.state.phase === "build" && options.state.approval !== "confirmed" ? " --confirmed" : ""; + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "advance-phase", + command: `comet native next ${options.state.name} --summary ""${confirmationSuffix}`, + requiresUserDecision: false, + requiredInputs: requiredPhaseInputs(options.state) + }; +} + +// domains/comet-native/native-findings.ts +import path36 from "path"; +var FINDING_SUMMARY_CODE_BUDGET = 8; +var EXACT_METADATA = { + "brief-blocking-question": { + severity: "error", + requiredAction: "answer-blocking-question", + retry: "next", + repair: "none" + }, + "shape-confirmation-required": { + severity: "error", + requiredAction: "confirm-shared-understanding", + retry: "next", + repair: "none" + }, + "approval-confirmation-required": { + severity: "error", + requiredAction: "confirm-shared-understanding", + retry: "next", + repair: "none" + }, + "transition-incomplete": { + severity: "error", + requiredAction: "recover-transition", + retry: "status", + repair: "doctor" + }, + "trajectory-tail-incomplete": { + severity: "error", + requiredAction: "repair-trajectory-tail", + retry: "status", + repair: "doctor" + }, + "checkpoint-progress-invalid": { + severity: "error", + requiredAction: "manually-isolate-invalid-checkpoint", + retry: "none", + repair: "none" + }, + "checkpoint-progress-incomplete": { + severity: "error", + requiredAction: "recover-progress-checkpoint", + retry: "status", + repair: "doctor" + }, + "checkpoint-manifest-invalid": { + severity: "error", + requiredAction: "record-checkpoint-again", + retry: "status", + repair: "none" + }, + "verification-scope-partial": { + severity: "error", + requiredAction: "confirm-partial-verification-scope", + retry: "next", + repair: "none" + }, + "native-change-conflict": { + severity: "error", + requiredAction: "resolve-native-change-conflict", + retry: "status", + repair: "none" + }, + "native-change-overlap": { + severity: "error", + requiredAction: "inspect-native-change-overlap", + retry: "status", + repair: "none" + }, + "contract-changed-after-approval": { + severity: "error", + requiredAction: "re-confirm-contract", + retry: "next", + repair: "none" + }, + "baseline-snapshot-incomplete": { + severity: "error", + requiredAction: "resolve-native-baseline", + retry: "none", + repair: "none" + }, + "baseline-snapshot-missing": { + severity: "error", + requiredAction: "resolve-native-baseline", + retry: "none", + repair: "none" + }, + "workspace-inspection-unavailable": { + severity: "info", + requiredAction: "migrate-workspace-identity", + retry: "status", + repair: "doctor" + }, + "repair-stagnation-warning": { + severity: "warning", + requiredAction: "change-repair-approach", + retry: "next", + repair: "none" + }, + "repair-stagnation-stop": { + severity: "error", + requiredAction: "make-progress-or-explicitly-override-repair", + retry: "none", + repair: "none" + }, + "repair-iteration-limit": { + severity: "error", + requiredAction: "change-implementation-before-starting-a-new-repair-episode", + retry: "next", + repair: "none" + }, + "repair-override-exhausted": { + severity: "error", + requiredAction: "review-repeated-failure-after-override", + retry: "none", + repair: "none" + } +}; +function inferredMetadata(code) { + const exact = EXACT_METADATA[code]; + if (exact) return exact; + if (/^(?:run-|trajectory-|checkpoint-(?:missing|mismatch|invalid)|transition-invalid)/u.test(code)) { + return { + severity: "error", + requiredAction: "isolate-or-restore-native-runtime-from-a-trusted-copy", + retry: "none", + repair: "none" + }; + } + if (code.startsWith("brief-")) { + return { + severity: "error", + requiredAction: "complete-brief", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("spec-")) { + return { + severity: "error", + requiredAction: "resolve-spec-state", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("verification-")) { + return { + severity: "error", + requiredAction: "complete-verification-evidence", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("build-")) { + return { + severity: "error", + requiredAction: "record-build-evidence", + retry: "next", + repair: "none" + }; + } + if (isNativeWorkspaceAdvisoryCode(code)) { + return { + severity: code === "workspace-inspection-unavailable" ? "info" : "warning", + requiredAction: "inspect-workspace-advisory", + retry: "status", + repair: "none" + }; + } + return { + severity: "error", + requiredAction: "resolve-finding", + retry: "status", + repair: "none" + }; +} +function projectRelativePath3(paths, state, finding) { + if (!finding.path) return null; + let target; + if (path36.isAbsolute(finding.path)) { + target = path36.resolve(finding.path); + } else if (/^(?:brief-|verification-|spec-source)/u.test(finding.code)) { + target = path36.resolve(nativeChangeDir(paths, state.name), ...finding.path.split(/[\\/]/u)); + } else { + target = path36.resolve(paths.projectRoot, ...finding.path.split(/[\\/]/u)); + } + if (!isInsidePath(paths.projectRoot, target)) return null; + const relative = path36.relative(paths.projectRoot, target).replaceAll("\\", "/"); + return relative === "" ? "." : relative; +} +function retryCommand(retry, state, code) { + if (retry === "next") { + return `comet native next ${state.name} --summary ""${code === "contract-changed-after-approval" || code === "shape-confirmation-required" || code === "approval-confirmation-required" ? " --confirmed" : ""}`; + } + if (retry === "status") return `comet native status ${state.name} --details`; + return null; +} +function structureNativeFindings(options) { + return options.findings.map((finding) => { + const metadata = inferredMetadata(finding.code); + return { + code: finding.code, + message: finding.message, + severity: metadata.severity, + path: projectRelativePath3(options.paths, options.state, finding), + requiredAction: metadata.requiredAction, + retryCommand: retryCommand(metadata.retry, options.state, finding.code), + repairCommand: metadata.repair === "doctor" ? `comet native doctor ${options.state.name} --repair${finding.code.startsWith("transition-") ? " --strategy continue" : ""}` : null, + // This is intentionally code-based, not severity-based. Model-actionable + // missing data must never be presented as a user decision. + requiresUserDecision: finding.code === "brief-blocking-question" || finding.code === "shape-confirmation-required" || finding.code === "approval-confirmation-required" || finding.code === "contract-changed-after-approval" || finding.code === "verification-scope-partial" || finding.code === "repair-iteration-limit" || finding.code === "repair-override-exhausted" + }; + }).sort((left, right) => { + const severityRank = { error: 0, warning: 1, info: 2 }; + return severityRank[left.severity] - severityRank[right.severity] || left.code.localeCompare(right.code) || (left.path ?? "").localeCompare(right.path ?? "") || left.message.localeCompare(right.message); + }); +} +function summarizeNativeFindings(findings) { + const codes = [...new Set(findings.map((finding) => finding.code))]; + return { + total: findings.length, + errors: findings.filter((finding) => finding.severity === "error").length, + warnings: findings.filter((finding) => finding.severity === "warning").length, + info: findings.filter((finding) => finding.severity === "info").length, + requiresUserDecision: findings.some((finding) => finding.requiresUserDecision), + codes: codes.slice(0, FINDING_SUMMARY_CODE_BUDGET), + truncated: codes.length > FINDING_SUMMARY_CODE_BUDGET + }; +} + +// domains/comet-native/native-resume-view.ts +import path37 from "path"; +var COMPACT_TEXT_BUDGET = 240; +var COMPACT_REASON_CODE_BUDGET = 8; +var NATIVE_INSPECTION_REASON_DETAIL_BUDGET = 50; +function compactText(value) { + const characters = Array.from(value); + return characters.length <= COMPACT_TEXT_BUDGET ? value : `${characters.slice(0, COMPACT_TEXT_BUDGET - 1).join("")}…`; +} +function reasonCode(reason) { + const separator = reason.indexOf(":"); + return separator < 0 ? reason : reason.slice(0, separator); +} +function inspectionViews(reasons) { + const codes = [...new Set(reasons.map(reasonCode))]; + const inspection = { + freshness: reasons.length === 0 || reasons.length === 1 && reasons[0] === "no-checkpoint" ? "fresh" : "stale", + codes: codes.slice(0, COMPACT_REASON_CODE_BUDGET), + reasonCount: reasons.length, + codesTruncated: codes.length > COMPACT_REASON_CODE_BUDGET + }; + return { + inspection, + inspectionDetails: { + ...inspection, + reasons: reasons.slice(0, NATIVE_INSPECTION_REASON_DETAIL_BUDGET), + reasonsTruncated: reasons.length > NATIVE_INSPECTION_REASON_DETAIL_BUDGET + } + }; +} +async function buildNativeResumeView(options) { + let pendingFinding = null; + try { + const pending = await readNativeCheckpointJournal(options.paths, options.state.name); + if (pending) { + pendingFinding = { + code: "checkpoint-progress-incomplete", + message: `Native progress checkpoint ${pending.id} requires deterministic recovery`, + path: nativeCheckpointJournalFile(options.paths, options.state.name) + }; + } + } catch (error) { + pendingFinding = { + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint journal is invalid: ${error.message}. Automatic repair is unavailable; inspect and move the invalid checkpoint journal aside before retrying`, + path: nativeCheckpointJournalFile(options.paths, options.state.name) + }; + } + const inspected = await inspectNativeCheckpointFreshness({ + paths: options.paths, + name: options.state.name, + stateRevision: options.state.revision + }); + const allReasons = pendingFinding ? [pendingFinding.code, ...inspected.reasons] : inspected.reasons; + const views = inspectionViews(allReasons); + if (!inspected.checkpoint) { + return { + inspection: views.inspection, + inspectionDetails: views.inspectionDetails, + checkpoint: null, + checkpointDetails: null, + findings: pendingFinding ? [pendingFinding, ...inspected.findings] : inspected.findings, + maxCheckpointArtifacts: NATIVE_CHECKPOINT_LIMITS.maxArtifacts + }; + } + const compact = { + id: inspected.checkpoint.id, + createdAt: inspected.checkpoint.createdAt, + phase: inspected.checkpoint.phase, + stateRevision: inspected.checkpoint.stateRevision, + summary: compactText(inspected.checkpoint.summary), + nextAction: compactText(inspected.checkpoint.nextAction), + artifactCount: inspected.checkpoint.artifactCount + }; + const details = inspected.manifest ? { + ...compact, + summary: inspected.checkpoint.summary, + nextAction: inspected.checkpoint.nextAction, + manifestHash: inspected.checkpoint.manifestHash, + manifestRef: path37.relative( + options.paths.projectRoot, + path37.join( + nativeChangeDir(options.paths, options.state.name), + ...inspected.checkpoint.manifestRef.split("/") + ) + ).replaceAll("\\", "/"), + artifacts: inspected.manifest.artifacts, + totalBytes: inspected.manifest.totalBytes + } : null; + return { + inspection: views.inspection, + inspectionDetails: views.inspectionDetails, + checkpoint: compact, + checkpointDetails: details, + findings: pendingFinding ? [pendingFinding, ...inspected.findings] : inspected.findings, + maxCheckpointArtifacts: NATIVE_CHECKPOINT_LIMITS.maxArtifacts + }; +} + +// domains/comet-native/native-repair-integration.ts +function assertRepairableBuildState(state) { + if (state.phase !== "build" || state.verification_result !== "fail" || !state.implementation_scope || !state.verification_evidence) { + throw new Error("Native repair guard requires a failed Verify-to-Build state"); + } +} +async function readNativeCommittedRepairTrajectory(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const run = await readNativeRunState(changeDir); + if (!run || !state.run_id || run.runId !== state.run_id) { + throw new Error("Native repair history Run state is missing or mismatched"); + } + const checkpoint = await readNativeCheckpoint(changeDir, run.checkpointRef); + if (!checkpoint || checkpoint.runId !== run.runId || checkpoint.stateVersion !== run.iteration) { + throw new Error("Native repair history checkpoint is missing or mismatched"); + } + return { + trajectory: await readNativeTrajectory(changeDir, run.trajectoryRef), + committedTrajectoryOffset: checkpoint.trajectoryOffset, + runId: run.runId + }; +} +async function inspectNativeRepairHistory(paths, state) { + const committed = await readNativeCommittedRepairTrajectory(paths, state); + return { + committed, + latest: inspectLatestNativeRepairProjection(committed), + history: rebuildNativeRepairHistory(committed) + }; +} +function decisionFromInspection(inspection) { + const latest = inspection.latest; + if (!latest) return null; + const failures = inspection.history.filter((entry2) => entry2.kind === "failure"); + let consecutiveFailures = 0; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== latest.signatureHash) break; + consecutiveFailures += 1; + } + const overrideAccepted = latest.overrideSummaryHash !== null; + const overrideAlreadyUsed = inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ); + return { + disposition: latest.disposition, + reasonCode: overrideAccepted ? "override-accepted" : overrideAlreadyUsed && latest.disposition === "manual-stop" ? "override-already-used" : latest.disposition === "hard-stop" ? "repair-iteration-limit" : latest.disposition === "manual-stop" ? "repeated-failure-stop" : latest.disposition === "warn" ? "repeated-failure-warning" : "new-failure-signature", + signatureHash: latest.signatureHash, + consecutiveFailures, + totalRepairFailures: failures.length, + remainingIterations: Math.max( + 0, + NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations - failures.length + ), + overrideAccepted + }; +} +async function inspectLatestNativeRepairDecision(paths, state) { + return decisionFromInspection(await inspectNativeRepairHistory(paths, state)); +} +function projectNativeRepairDecision(result2) { + return { + disposition: result2.decision.disposition, + reasonCode: result2.decision.reasonCode, + signatureHash: result2.decision.signature.signatureHash, + consecutiveFailures: result2.decision.consecutiveFailures, + totalRepairFailures: result2.decision.totalRepairFailures, + remainingIterations: result2.decision.remainingIterations, + overrideAccepted: result2.decision.overrideAccepted + }; +} +async function inspectNativeRepairFailureForTransition(options) { + if (!options.state.implementation_scope) { + throw new Error("Native repair failure has no implementation scope"); + } + const [committed, implementationScope] = await Promise.all([ + readNativeCommittedRepairTrajectory(options.paths, options.state), + readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ) + ]); + return inspectNativeRepairFailure({ + ...committed, + envelope: options.envelope, + implementationScope, + ...options.categories ? { categories: options.categories } : {}, + ...options.failedCheckIds ? { failedCheckIds: options.failedCheckIds } : {} + }); +} +async function inspectNativeRepairBuildGuard(options) { + const inspection = await inspectNativeRepairHistory(options.paths, options.state); + const latest = inspection.latest; + if (!latest || latest.disposition !== "manual-stop" && latest.disposition !== "hard-stop") { + if (options.override) { + throw new Error("Native repair override requires the latest manual stop"); + } + return { disposition: "proceed", eventProjection: null }; + } + const overrideRecorded = inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ); + const activeFailedRepair = options.state.phase === "build" && options.state.verification_result === "fail" && options.state.implementation_scope !== null && options.state.verification_evidence !== null; + if (!activeFailedRepair) { + if (options.override) { + throw new Error("Native repair override requires an active failed Verify-to-Build state"); + } + return { disposition: "proceed", eventProjection: null }; + } + assertRepairableBuildState(options.state); + const [previousEnvelope, previousImplementationScope] = await Promise.all([ + readNativeVerificationEvidence( + options.paths, + options.state.name, + options.state.verification_evidence + ), + readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ) + ]); + if (previousImplementationScope.scope.scopeHash !== previousEnvelope.implementationScopeHash) { + throw new Error("Native repair verification evidence does not match its implementation scope"); + } + if (nativeRepairScopeHash(options.currentImplementationScope) !== nativeRepairScopeHash(previousImplementationScope)) { + if (options.override) { + throw new Error("Native repair override is not valid after implementation scope progress"); + } + return { disposition: "proceed", eventProjection: null }; + } + if (latest.disposition === "hard-stop") { + if (options.override) { + throw new Error( + "Native repair hard stop cannot be overridden without implementation progress" + ); + } + return { + disposition: "hard-stop", + signatureHash: latest.signatureHash, + overrideRecorded + }; + } + if (overrideRecorded) { + return { + disposition: "hard-stop", + signatureHash: latest.signatureHash, + overrideRecorded + }; + } + if (!options.override) { + return { + disposition: "manual-stop", + signatureHash: latest.signatureHash, + overrideRecorded: false + }; + } + return { + disposition: "proceed", + eventProjection: acceptLatestNativeRepairOverride({ + ...inspection.committed, + override: options.override + }).eventProjection + }; +} +async function inspectNativeRepairStatus(paths, state) { + if (state.phase !== "build" || state.verification_result !== "fail") return null; + const inspection = await inspectNativeRepairHistory(paths, state); + const latest = inspection.latest; + if (!latest || latest.disposition !== "manual-stop" && latest.disposition !== "hard-stop") { + return null; + } + return { + disposition: latest.disposition, + signatureHash: latest.signatureHash, + overrideRecorded: inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ) + }; +} + +// domains/comet-native/native-diagnostics.ts +var NATIVE_STATUS_CURSOR_PATTERN = /^native-status-v1\.([a-f0-9]{64})\.([0-9a-z]+)\.([a-f0-9]{64})$/u; +var NATIVE_STATUS_PAGE_LIMITS = Object.freeze({ + maxItems: 24, + maxChanges: 4096, + maxSerializedBytes: 512 * 1024 +}); +async function selectedName(paths) { + try { + return (await readNativeSelectionRecord(paths))?.change ?? null; + } catch { + return null; + } +} +function nativeNextCommand(state, archiveReady, evidenceRetreat = false, _clarificationMode) { + if (state.phase === "archive") { + return archiveReady ? `comet native archive ${state.name} --dry-run` : evidenceRetreat ? `comet native next ${state.name} --summary ""` : null; + } + return `comet native next ${state.name} --summary ""${state.phase === "shape" || state.phase === "build" && state.approval !== "confirmed" ? " --confirmed" : ""}`; +} +async function statusFindings(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const findings = [ + ...(await validateNativeBrief(changeDir, state.brief)).findings, + ...(await validateNativeSpecChanges(paths, state)).findings, + ...await inspectNativeRunConsistency(paths, state) + ]; + if (state.phase === "shape" || state.phase === "build") { + try { + const capturedBaseline = await readNativeBaselineManifest(paths, state.name); + if (capturedBaseline === null) { + findings.push({ + code: "baseline-snapshot-missing", + message: "Native baseline is missing; restore a trusted baseline before advancing" + }); + } else { + const baseline = await filterNativeContentSnapshotToProjectScope(paths, capturedBaseline); + if (!baseline.complete) { + findings.push({ + code: "baseline-snapshot-incomplete", + message: `Native baseline is incomplete within the project-owned scope (${baseline.omittedCount} omitted entries); resolve the omissions before advancing` + }); + } + } + } catch (error) { + findings.push({ + code: "baseline-snapshot-invalid", + message: `Native baseline could not be inspected safely: ${error.message}` + }); + } + } + if (state.phase === "build") { + try { + const current = await collectNativeContractFiles({ + changeDir, + briefRef: state.brief, + specChanges: state.spec_changes + }); + if ((state.approved_contract_hash ?? null) !== current.contract.contractHash) { + findings.push({ + code: "contract-changed-after-approval", + message: state.approval === null || state.approved_contract_hash === null || state.approved_contract_hash === void 0 ? "Native approval is not bound to a contract hash; re-confirm the current contract" : "Native contract changed after approval; re-confirm the current contract" + }); + } + } catch (error) { + findings.push({ + code: "contract-inspection-invalid", + message: `Native approved contract could not be inspected safely: ${error.message}` + }); + } + } + try { + if (await inspectPendingNativeTransition(paths, state.name)) { + findings.unshift({ + code: "transition-incomplete", + message: "Native phase transition recovery is pending" + }); + } + } catch (error) { + findings.unshift({ + code: "transition-invalid", + message: `Native transition journal is invalid: ${error.message}` + }); + } + if (state.verification_report) { + findings.push( + ...(await validateNativeVerification(changeDir, state.verification_report)).findings + ); + } else if (state.phase === "verify" || state.phase === "archive" || state.verification_result === "pass") { + findings.push({ + code: "verification-report-missing", + message: "Native change has no verification report" + }); + } + return findings; +} +async function inspectNativeStatus(paths, name, options) { + const selected = await selectedName(paths) === name; + let state; + try { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required" && inspection.state) { + return { + name, + phase: inspection.state.phase, + revision: "revision" in inspection.state ? inspection.state.revision : null, + approval: inspection.state.approval, + verificationResult: inspection.state.verification_result, + specChanges: inspection.state.spec_changes.length, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["migration-required"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + schema: inspection.schema, + migrationRequired: true, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + error: inspection.message + }; + } + if (inspection.status !== "current" || !inspection.state) { + return { + name, + phase: "invalid", + revision: null, + approval: null, + verificationResult: "pending", + specChanges: 0, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["runtime-incompatible"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + error: inspection.message ?? `Native change ${name} is incompatible` + }; + } + state = inspection.state; + } catch (error) { + return { + name, + phase: "invalid", + revision: null, + approval: null, + verificationResult: "pending", + specChanges: 0, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["change-invalid"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + error: error.message + }; + } + const resume = await buildNativeResumeView({ paths, state }); + let acceptancePage; + if (options?.details && (state.phase === "verify" || state.phase === "archive")) { + try { + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(paths, state.name), + briefRef: state.brief, + specChanges: state.spec_changes + }); + acceptancePage = projectNativeAcceptancePage({ + criteria: contract.contract.acceptance, + acceptanceHash: contract.contract.acceptanceHash, + ...options.acceptanceCursor ? { cursor: options.acceptanceCursor } : {} + }); + } catch (error) { + if (options.acceptanceCursor) throw error; + acceptancePage = void 0; + } + } + const conflictFindings = []; + try { + const conflicts = await inspectNativeChangeConflicts(paths, state.name); + conflictFindings.push( + ...conflicts.findingCodes.map((code) => ({ + code, + message: `Native change overlap is visible in the current root: ${code}` + })) + ); + } catch { + conflictFindings.push({ + code: "native-conflict-inspection-invalid", + message: "Native change overlap could not be recomputed safely" + }); + } + const workspaceFindings = []; + try { + const identity = await readNativeWorkspaceIdentity(paths, state.name); + if (identity) { + const workspace = await inspectNativeWorkspaceAdvisory({ + paths, + identity + }); + workspaceFindings.push( + ...workspace.findingCodes.map((code) => ({ + code, + message: `Native workspace advisory changed: ${code} (${workspace.driftComponents.join(", ") || "no-component"})` + })) + ); + } + } catch { + workspaceFindings.push({ + code: "workspace-inspection-unavailable", + message: "Native workspace advisory could not be recomputed safely" + }); + } + const verifyScopeFindings = []; + let verifyEvidenceRetreat = false; + if (state.phase === "verify") { + const freshness = await inspectNativeImplementationScopeFreshness({ paths, state }); + verifyEvidenceRetreat = freshness.freshness !== "fresh"; + verifyScopeFindings.push( + ...freshness.findingCodes.map((code) => ({ + code, + message: `Native Verify implementation scope is not current: ${code}` + })) + ); + } + let repair = null; + const repairFindings = []; + if (state.phase === "build" && state.verification_result === "fail") { + try { + repair = await inspectNativeRepairStatus(paths, state); + if (repair) { + const code = repair.disposition === "hard-stop" ? "repair-iteration-limit" : repair.overrideRecorded ? "repair-override-exhausted" : "repair-stagnation-stop"; + repairFindings.push({ + code, + message: `Native repair is stopped at failure signature: ${repair.signatureHash}` + }); + } + } catch { + repairFindings.push({ + code: "trajectory-invalid", + message: "Native repair history could not be reconstructed safely" + }); + } + } + let archivePreflight = null; + const archiveFindings = []; + if (state.phase === "archive") { + try { + archivePreflight = await inspectNativeArchivePreflight({ paths, name: state.name }); + archiveFindings.push( + ...archivePreflight.findingCodes.map((code) => ({ + code, + message: `Native Archive is blocked: ${code}` + })) + ); + } catch { + archiveFindings.push({ + code: "archive-preflight-invalid", + message: "Native Archive preflight could not be recomputed safely" + }); + } + } + const rawFindings = [ + ...await statusFindings(paths, state), + ...resume.findings, + ...conflictFindings, + ...workspaceFindings, + ...verifyScopeFindings, + ...repairFindings, + ...archiveFindings + ].filter( + (finding, index, values) => values.findIndex( + (candidate) => candidate.code === finding.code && candidate.path === finding.path + ) === index + ); + const findings = structureNativeFindings({ paths, state, findings: rawFindings }); + const archiveBlockingFindings = findings.filter( + (finding) => !isNativeWorkspaceAdvisoryCode(finding.code) + ); + const archiveReady = state.phase === "archive" && archivePreflight?.ready === true && archiveBlockingFindings.length === 0; + const evidenceRetreat = verifyEvidenceRetreat || state.phase === "archive" && (archivePreflight?.findingCodes ?? []).some( + (code) => (/* @__PURE__ */ new Set([ + "verification-evidence-stale", + "verification-evidence-invalid", + "verification-evidence-missing", + "verification-contract-stale", + "verification-implementation-stale", + "verification-report-stale", + "verification-state-mismatch" + ])).has(code) + ); + const mutationBlocked = findings.some( + (finding) => finding.code === "trajectory-tail-incomplete" || finding.code === "trajectory-invalid" + ); + const repairBlocked = repair !== null; + const firstErrorFinding = findings.find((finding) => finding.severity === "error"); + return { + name: state.name, + phase: state.phase, + revision: state.revision, + approval: state.approval, + verificationResult: state.verification_result, + specChanges: state.spec_changes.length, + selected, + nextCommand: mutationBlocked || repairBlocked ? null : nativeNextCommand(state, archiveReady, evidenceRetreat, options?.clarificationMode), + archiveReady, + inspection: resume.inspection, + findingSummary: summarizeNativeFindings(findings), + detailsCommand: `comet native status ${state.name} --details`, + checkpoint: resume.checkpoint, + continuation: nativeContinuation({ + state, + findings, + archiveReady, + evidenceRetreat, + clarificationMode: options?.clarificationMode + }), + repair, + ...options?.details ? { + ...acceptancePage ? { acceptancePage } : {}, + findings: findings.slice(0, 50), + inspectionDetails: resume.inspectionDetails, + checkpointDetails: resume.checkpointDetails, + budgets: { + maxFindings: 50, + maxInspectionReasons: NATIVE_INSPECTION_REASON_DETAIL_BUDGET, + maxCheckpointArtifacts: resume.maxCheckpointArtifacts, + findingsTruncated: findings.length > 50, + inspectionReasonsTruncated: resume.inspectionDetails.reasonsTruncated, + checkpointArtifactsTruncated: false + } + } : {}, + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + ...firstErrorFinding ? { error: firstErrorFinding.message } : {} + }; +} +async function boundedNativeChangeNames(paths) { + let guard; + try { + guard = await captureNativeProtectedDirectoryGuard({ + root: paths.nativeRoot, + directory: paths.changesDir, + label: "Native status changes directory" + }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const names = []; + const directory = await fs24.opendir(paths.changesDir); + try { + for await (const entry2 of directory) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + names.push(entry2.name); + if (names.length > NATIVE_STATUS_PAGE_LIMITS.maxChanges) { + throw new Error( + `Native status exceeds ${NATIVE_STATUS_PAGE_LIMITS.maxChanges} visible changes` + ); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + await guard.verify(); + return names.sort(); +} +function nativeStatusCursor(namesHash, offset) { + const encodedOffset = offset.toString(36); + const integrity = canonicalHash("comet.native.status-cursor.v1", { namesHash, offset }); + return `native-status-v1.${namesHash}.${encodedOffset}.${integrity}`; +} +function nativeStatusOffset(options) { + if (options.cursor === void 0 || options.cursor === null) return 0; + const match = NATIVE_STATUS_CURSOR_PATTERN.exec(options.cursor); + if (!match) throw new Error("Native status cursor is invalid"); + if (match[1] !== options.namesHash) throw new Error("Native status cursor is stale"); + const offset = Number.parseInt(match[2], 36); + if (!Number.isSafeInteger(offset) || offset <= 0 || offset >= options.total || offset.toString(36) !== match[2]) { + throw new Error("Native status cursor offset is invalid"); + } + const expected = canonicalHash("comet.native.status-cursor.v1", { + namesHash: options.namesHash, + offset + }); + if (match[3] !== expected) throw new Error("Native status cursor integrity check failed"); + return offset; +} +async function listNativeStatusPage(paths, options) { + const names = await boundedNativeChangeNames(paths); + const namesHash = canonicalHash("comet.native.status-names.v1", names); + const offset = nativeStatusOffset({ + namesHash, + total: names.length, + cursor: options?.cursor + }); + const candidates = await Promise.all( + names.slice(offset, offset + NATIVE_STATUS_PAGE_LIMITS.maxItems).map( + (name) => inspectNativeStatus(paths, name, { + clarificationMode: options?.clarificationMode + }) + ) + ); + const items = []; + for (const candidate of candidates) { + const trialItems = [...items, candidate]; + const nextOffset2 = offset + trialItems.length; + const trial = { + schema: "comet.native.status-page.v1", + total: names.length, + offset, + items: trialItems, + nextCursor: nextOffset2 < names.length ? nativeStatusCursor(namesHash, nextOffset2) : null, + limits: { ...NATIVE_STATUS_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(trial), "utf8") > NATIVE_STATUS_PAGE_LIMITS.maxSerializedBytes) { + if (items.length === 0) { + throw new Error("Native status item exceeds its page serialization budget"); + } + break; + } + items.push(candidate); + } + const nextOffset = offset + items.length; + return { + schema: "comet.native.status-page.v1", + total: names.length, + offset, + items, + nextCursor: nextOffset < names.length ? nativeStatusCursor(namesHash, nextOffset) : null, + limits: { ...NATIVE_STATUS_PAGE_LIMITS } + }; +} +async function listNativeStatus(paths, options) { + return (await listNativeStatusPage(paths, { + clarificationMode: options?.clarificationMode + })).items; +} + +// domains/comet-native/native-doctor.ts +import { promises as fs28 } from "fs"; +import path41 from "path"; + +// domains/comet-native/native-evidence-retention.ts +import { createHash as createHash13, randomUUID as randomUUID8 } from "node:crypto"; +import { constants as fsConstants5, promises as fs26 } from "node:fs"; +import path39 from "node:path"; + +// domains/comet-native/native-schema-migration.ts +var import_yaml4 = __toESM(require_dist(), 1); +import { randomUUID as randomUUID7 } from "crypto"; +import { promises as fs25 } from "fs"; +import path38 from "path"; +import { isDeepStrictEqual as isDeepStrictEqual3 } from "util"; +var HASH_PATTERN14 = /^[a-f0-9]{64}$/u; +var MIGRATION_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "change", + "fromSchema", + "toSchema", + "sourceHash", + "targetHash", + "createdAt", + "nextState", + "transition", + "transitionSupersede", + "runRetreat" +]); +var MIGRATION_TRANSITION_KEYS = /* @__PURE__ */ new Set(["sourceHash", "targetHash", "nextJournal"]); +var MIGRATION_TRANSITION_SUPERSEDE_KEYS = /* @__PURE__ */ new Set([ + "sourceHash", + "transitionId", + "previousRun", + "nextRun", + "evidenceHash", + "eventData" +]); +var MIGRATION_SUPERSEDE_EVENT_KEYS = /* @__PURE__ */ new Set([ + "fromSchema", + "toSchema", + "previousPhase", + "nextPhase", + "reason", + "supersededTransitionId" +]); +var MIGRATION_RUN_RETREAT_KEYS = /* @__PURE__ */ new Set(["previousRun", "nextRun", "evidenceHash", "eventData"]); +function rejectUnknownFields(value, keys, label) { + const unknown = Object.keys(value).find((key) => !keys.has(key)); + if (unknown) throw new Error(`${label} contains unknown field: ${unknown}`); +} +function transitionContent(journal) { + return JSON.stringify(journal, null, 2) + "\n"; +} +function migrationStateDocument(state) { + return state.schema === NATIVE_V2_CHANGE_SCHEMA ? nativeV2ChangeDocument(state) : nativeChangeDocument(state); +} +function upgradeV1StateToV2(state, revision) { + return { + ...state, + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision + }; +} +function upgradeV1TransitionToV2(journal) { + return { + ...journal, + schema: NATIVE_V2_TRANSITION_SCHEMA, + minimum_runtime_version: 2, + revision: 1, + previousState: upgradeV1StateToV2(journal.previousState, 1), + nextState: upgradeV1StateToV2(journal.nextState, 2) + }; +} +function upgradeV2StateToV3(state, options) { + const retreat = options?.retreatEvidencePhase === true && (state.phase === "verify" || state.phase === "archive"); + return { + ...state, + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: state.revision + (retreat && options?.incrementRetreatRevision ? 1 : 0), + phase: retreat ? "build" : state.phase, + verification_result: retreat ? "pending" : state.verification_result, + verification_report: retreat ? null : state.verification_report, + approved_contract_hash: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: retreat ? false : state.archived + }; +} +function upgradeV2TransitionToV3(journal) { + if (journal.nextState.phase === "archive") { + throw new Error("Native v2 Archive transition must be superseded by schema migration"); + } + const specRebase = journal.previousState.phase !== "shape" && journal.nextState.phase === "build" && journal.eventData.verificationResult === null; + const evidenceHash = specRebase ? sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: journal.change, + summary: journal.eventData.summary, + specChanges: journal.nextState.spec_changes + }) + ) : journal.evidenceHash; + return { + ...journal, + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + operation: specRebase ? "spec-rebase" : "advance", + evidenceHash, + previousState: upgradeV2StateToV3(journal.previousState), + nextState: { + ...upgradeV2StateToV3(journal.nextState), + ...specRebase ? { + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + } : {} + }, + eventData: { ...journal.eventData, evidenceHash }, + nextRun: journal.previousRun === null ? { + ...journal.nextRun, + skillVersion: NATIVE_RUNTIME_PACKAGE.definition.metadata.version, + skillHash: NATIVE_RUNTIME_HASH + } : journal.nextRun + }; +} +function sameV1State(left, right) { + return JSON.stringify(left) === JSON.stringify(right); +} +function sameV2State(left, right) { + return JSON.stringify(nativeV2ChangeDocument(left)) === JSON.stringify(nativeV2ChangeDocument(right)); +} +function sameCurrentState(left, right) { + return JSON.stringify(nativeChangeDocument(left)) === JSON.stringify(nativeChangeDocument(right)); +} +function sameRunState(left, right) { + return isDeepStrictEqual3(left, right); +} +function parseTransitionSupersede(value, expectedName, nextState, migrationId) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Schema migration transition supersede plan is invalid"); + } + const record8 = value; + rejectUnknownFields( + record8, + MIGRATION_TRANSITION_SUPERSEDE_KEYS, + "Schema migration transition supersede plan" + ); + if (typeof record8.sourceHash !== "string" || !HASH_PATTERN14.test(record8.sourceHash)) { + throw new Error("Schema migration transition supersede source hash is invalid"); + } + if (typeof record8.transitionId !== "string" || record8.transitionId.length === 0) { + throw new Error("Schema migration superseded transition id is invalid"); + } + if (typeof record8.evidenceHash !== "string" || !HASH_PATTERN14.test(record8.evidenceHash)) { + throw new Error("Schema migration transition supersede evidence hash is invalid"); + } + const previousRun = parseNativeStoredRunStateValue(record8.previousRun); + const nextRun = parseNativeStoredRunStateValue(record8.nextRun); + if (previousRun.runId !== nextRun.runId || nextState.run_id !== nextRun.runId || nextState.phase !== "build" && nextState.phase !== "verify" || nextRun.currentStep !== nextState.phase || nextRun.pending !== null || nextRun.status !== "running" || nextState.verification_result !== "pending" || nextState.verification_report !== null || nextState.implementation_scope !== null || nextState.verification_evidence !== null || nextState.partial_allowance !== null) { + throw new Error("Schema migration transition supersede Run does not match target state"); + } + const retreatAllowed = previousRun.currentStep === "verify" && nextState.phase === "build" || previousRun.currentStep === "archive" && nextState.phase === "build"; + const expectedNextRun = previousRun.currentStep === nextState.phase ? previousRun : retreatAllowed ? { + ...previousRun, + currentStep: nextState.phase, + iteration: previousRun.iteration + 1, + pending: null, + status: "running" + } : null; + if (!expectedNextRun || !sameRunState(expectedNextRun, nextRun)) { + throw new Error("Schema migration transition supersede Run retreat is invalid"); + } + if (!record8.eventData || typeof record8.eventData !== "object" || Array.isArray(record8.eventData)) { + throw new Error("Schema migration transition supersede event is invalid"); + } + const eventData = record8.eventData; + const eventKeys = Object.keys(eventData); + if (eventKeys.length !== MIGRATION_SUPERSEDE_EVENT_KEYS.size || eventKeys.some((key) => !MIGRATION_SUPERSEDE_EVENT_KEYS.has(key)) || eventData.fromSchema !== NATIVE_V2_CHANGE_SCHEMA || eventData.toSchema !== NATIVE_CHANGE_SCHEMA || !["build", "verify", "archive"].includes(eventData.previousPhase) || eventData.nextPhase !== nextState.phase || eventData.reason !== (nextState.phase === "build" ? "implementation-scope-required" : "verification-evidence-required") || eventData.supersededTransitionId !== record8.transitionId) { + throw new Error("Schema migration transition supersede event semantics are invalid"); + } + if (nextState.name !== expectedName) { + throw new Error("Schema migration transition supersede change mismatch"); + } + const expectedEvidenceHash = sha256Text( + JSON.stringify({ + operation: "supersede-v2-evidence-transition", + change: expectedName, + transitionId: record8.transitionId, + migrationId, + previousPhase: eventData.previousPhase, + nextPhase: eventData.nextPhase, + reason: eventData.reason, + previousIteration: previousRun.iteration, + nextRevision: nextState.revision + }) + ); + if (record8.evidenceHash !== expectedEvidenceHash) { + throw new Error("Schema migration transition supersede evidence hash mismatch"); + } + return { + sourceHash: record8.sourceHash, + transitionId: record8.transitionId, + previousRun, + nextRun, + evidenceHash: record8.evidenceHash, + eventData + }; +} +function nativeSchemaMigrationJournalFile(paths, name) { + return path38.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); +} +function parseMigrationJournal(value, expectedName) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native schema migration journal must be an object"); + } + rejectUnknownFields( + value, + MIGRATION_JOURNAL_KEYS, + "Native schema migration journal" + ); + const journal = value; + if (journal.schema !== "comet.native.schema-migration.v1") { + throw new Error("Unsupported Native schema migration journal"); + } + if (journal.change !== expectedName) throw new Error("Schema migration change mismatch"); + const v1ToV2 = journal.fromSchema === NATIVE_LEGACY_CHANGE_SCHEMA && journal.toSchema === NATIVE_V2_CHANGE_SCHEMA; + const v2ToV3 = journal.fromSchema === NATIVE_V2_CHANGE_SCHEMA && journal.toSchema === NATIVE_CHANGE_SCHEMA; + if (!v1ToV2 && !v2ToV3) throw new Error("Schema migration route is unsupported"); + if (typeof journal.id !== "string" || journal.id.length === 0) { + throw new Error("Schema migration id is invalid"); + } + if (typeof journal.sourceHash !== "string" || !HASH_PATTERN14.test(journal.sourceHash) || typeof journal.targetHash !== "string" || !HASH_PATTERN14.test(journal.targetHash)) { + throw new Error("Schema migration hash is invalid"); + } + if (typeof journal.createdAt !== "string" || Number.isNaN(Date.parse(journal.createdAt))) { + throw new Error("Schema migration timestamp is invalid"); + } + const nextState = v1ToV2 ? parseV2NativeChangeValue(journal.nextState) : parseNativeChangeValue(journal.nextState); + if (nextState.name !== expectedName) { + throw new Error("Schema migration target state change mismatch"); + } + if (sha256Text((0, import_yaml4.stringify)(migrationStateDocument(nextState))) !== journal.targetHash) { + throw new Error("Schema migration state target hash does not match its document"); + } + let transition; + if (journal.transition !== void 0) { + if (!journal.transition || typeof journal.transition !== "object") { + throw new Error("Schema migration transition target is invalid"); + } + rejectUnknownFields( + journal.transition, + MIGRATION_TRANSITION_KEYS, + "Schema migration transition target" + ); + const transitionValue = journal.transition; + if (typeof transitionValue.sourceHash !== "string" || !HASH_PATTERN14.test(transitionValue.sourceHash) || typeof transitionValue.targetHash !== "string" || !HASH_PATTERN14.test(transitionValue.targetHash)) { + throw new Error("Schema migration transition hash is invalid"); + } + const parsedNextJournal = v1ToV2 ? parseV2NativeTransitionJournalValue(transitionValue.nextJournal, expectedName) : parseNativeTransitionJournalValue(transitionValue.nextJournal, expectedName); + if (sha256Text(JSON.stringify(transitionValue.nextJournal, null, 2) + "\n") !== transitionValue.targetHash) { + throw new Error("Schema migration transition target hash does not match its journal"); + } + const matches = v1ToV2 && nextState.schema === NATIVE_V2_CHANGE_SCHEMA ? sameV2State(nextState, parsedNextJournal.previousState) || sameV2State(nextState, parsedNextJournal.nextState) : nextState.schema === NATIVE_CHANGE_SCHEMA && (sameCurrentState( + nextState, + parsedNextJournal.previousState + ) || sameCurrentState(nextState, parsedNextJournal.nextState)); + if (!matches) throw new Error("Schema migration state/transition target mismatch"); + transition = { + sourceHash: transitionValue.sourceHash, + targetHash: transitionValue.targetHash, + nextJournal: transitionValue.nextJournal + }; + } + if (v1ToV2 && (!transition && nextState.revision !== 1 || transition && nextState.revision !== 1 && nextState.revision !== 2)) { + throw new Error("Schema migration v1 target revision is invalid"); + } + let transitionSupersede; + if (journal.transitionSupersede !== void 0) { + if (!v2ToV3 || transition) { + throw new Error("Schema migration transition supersede plan is not valid for this route"); + } + if (nextState.schema !== NATIVE_CHANGE_SCHEMA) { + throw new Error("Schema migration transition supersede target schema is invalid"); + } + transitionSupersede = parseTransitionSupersede( + journal.transitionSupersede, + expectedName, + nextState, + journal.id + ); + } + let runRetreat; + if (journal.runRetreat !== void 0) { + if (!v2ToV3 || transition || transitionSupersede) { + throw new Error("Schema migration Run retreat is not valid for this route"); + } + if (!journal.runRetreat || typeof journal.runRetreat !== "object") { + throw new Error("Schema migration Run retreat is invalid"); + } + rejectUnknownFields( + journal.runRetreat, + MIGRATION_RUN_RETREAT_KEYS, + "Schema migration Run retreat" + ); + const retreat = journal.runRetreat; + let previousRun; + let nextRun; + try { + previousRun = parseNativeStoredRunStateValue(retreat.previousRun); + nextRun = parseNativeStoredRunStateValue(retreat.nextRun); + } catch (error) { + throw new Error("Schema migration Run retreat is invalid", { cause: error }); + } + if (typeof retreat.evidenceHash !== "string" || !HASH_PATTERN14.test(retreat.evidenceHash) || !retreat.eventData || typeof retreat.eventData !== "object" || Array.isArray(retreat.eventData)) { + throw new Error("Schema migration Run retreat is invalid"); + } + if (nextState.schema !== NATIVE_CHANGE_SCHEMA || nextState.phase !== "build" || nextState.run_id !== nextRun.runId || previousRun.runId !== nextRun.runId || previousRun.currentStep !== "verify" && previousRun.currentStep !== "archive" || nextRun.currentStep !== "build" || nextRun.iteration !== previousRun.iteration + 1) { + throw new Error("Schema migration Run retreat does not match target state"); + } + runRetreat = { + previousRun, + nextRun, + evidenceHash: retreat.evidenceHash, + eventData: retreat.eventData + }; + } + if (transition && runRetreat || transitionSupersede && runRetreat) { + throw new Error("Schema migration has conflicting recovery plans"); + } + return { + schema: "comet.native.schema-migration.v1", + id: journal.id, + change: expectedName, + fromSchema: journal.fromSchema, + toSchema: journal.toSchema, + sourceHash: journal.sourceHash, + targetHash: journal.targetHash, + createdAt: journal.createdAt, + nextState, + ...transition ? { transition } : {}, + ...transitionSupersede ? { transitionSupersede } : {}, + ...runRetreat ? { runRetreat } : {} + }; +} +async function inspectPendingNativeSchemaMigration(paths, name) { + const file = nativeSchemaMigrationJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const source = await readNativeProtectedTextFile({ + root: paths.nativeRoot, + file, + maxBytes: 512 * 1024, + label: "Native schema migration journal" + }); + return parseMigrationJournal(JSON.parse(source.text), name); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function ensureMigrationBaseline(paths, name, createdAt) { + const stored = await readNativeBaselineManifest(paths, name); + const baseline = stored ? await filterNativeContentSnapshotToProjectScope(paths, stored) : await createNativeContentSnapshot(paths, { + now: new Date(createdAt), + origin: "legacy-migration" + }); + if (!baseline.complete) { + const health = inspectNativeContentSnapshotHealth(baseline); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + throw new NativeBaselineIncompleteError( + name, + baseline.omittedCount, + omittedByReason, + health.samplePaths, + health.sampleTruncated + ); + } + if (stored === null) await writeNativeBaselineManifest(paths, name, baseline); +} +async function optionalFileHash(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +function expectedSupersedeEvent(journal, sequence) { + const supersede = journal.transitionSupersede; + return { + sequence, + timestamp: journal.createdAt, + type: "state_migrated", + runId: supersede.nextRun.runId, + data: { + ...supersede.eventData, + migrationId: journal.id, + evidenceHash: supersede.evidenceHash + } + }; +} +async function inspectSupersedeEvent(paths, journal) { + const supersede = journal.transitionSupersede; + const changeDir = nativeChangeDir(paths, journal.change); + const trajectory = await readNativeTrajectory(changeDir, supersede.nextRun.trajectoryRef); + const matches = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.migrationId === journal.id); + if (matches.length > 1) { + throw new Error(`Native schema migration event ${journal.id} is duplicated`); + } + const match = matches[0]; + if (match && !isDeepStrictEqual3(match.event, expectedSupersedeEvent(journal, match.index + 1))) { + throw new Error(`Native schema migration event ${journal.id} changed`); + } + return { trajectoryLength: trajectory.length, sequence: match?.event.sequence ?? null }; +} +async function assertSupersedeSourceBeforeMutation(paths, journal, stateAtTarget) { + if (!journal.transitionSupersede) return; + const transitionFile = nativeTransitionJournalFile(paths, journal.change); + const actualHash = await optionalFileHash(transitionFile); + if (actualHash === journal.transitionSupersede.sourceHash) return; + if (actualHash !== null) { + throw new Error( + `Native superseded transition source changed for ${journal.change}: expected ${journal.transitionSupersede.sourceHash}, actual ${actualHash}` + ); + } + const run = await readNativeRunState(nativeChangeDir(paths, journal.change)); + const event = await inspectSupersedeEvent(paths, journal); + const checkpoint = await readNativeCheckpoint( + nativeChangeDir(paths, journal.change), + journal.transitionSupersede.nextRun.checkpointRef + ); + if (!stateAtTarget || !run || !sameRunState(run, journal.transitionSupersede.nextRun) || event.sequence === null || !checkpoint || checkpoint.runId !== journal.transitionSupersede.nextRun.runId || checkpoint.stateVersion !== journal.transitionSupersede.nextRun.iteration || checkpoint.trajectoryOffset !== event.sequence || checkpoint.contextHash !== null || checkpoint.artifactsHash !== sha256Text(journal.transitionSupersede.evidenceHash) || checkpoint.createdAt !== journal.createdAt) { + throw new Error( + `Native superseded transition disappeared before migration ${journal.id} was durable` + ); + } +} +async function continueTransitionSupersede(paths, journal, hooks) { + const supersede = journal.transitionSupersede; + if (!supersede) return; + const changeDir = nativeChangeDir(paths, journal.change); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) { + throw new Error(`Native schema migration Run state disappeared for ${journal.change}`); + } + if (!sameRunState(currentRun, supersede.nextRun)) { + if (!sameRunState(currentRun, supersede.previousRun)) { + throw new Error(`Native schema migration Run source changed for ${journal.change}`); + } + await writeNativeRunState(changeDir, supersede.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + if (!sameRunState(await readNativeRunState(changeDir), supersede.nextRun)) { + throw new Error(`Native schema migration Run write diverged for ${journal.change}`); + } + let event = await inspectSupersedeEvent(paths, journal); + if (event.sequence === null) { + const appended = await appendNativeTrajectoryEvent({ + changeDir, + run: supersede.nextRun, + type: "state_migrated", + data: { + ...supersede.eventData, + migrationId: journal.id, + evidenceHash: supersede.evidenceHash + }, + now: new Date(journal.createdAt) + }); + event = { trajectoryLength: appended.sequence, sequence: appended.sequence }; + } + await hooks?.afterTrajectoryWritten?.(journal); + await writeNativeCheckpoint2({ + changeDir, + run: supersede.nextRun, + trajectoryOffset: event.sequence, + evidenceHash: supersede.evidenceHash, + now: new Date(journal.createdAt) + }); + await hooks?.afterCheckpointWritten?.(journal); + const transitionFile = nativeTransitionJournalFile(paths, journal.change); + const transitionHash = await optionalFileHash(transitionFile); + if (transitionHash !== null) { + if (transitionHash !== supersede.sourceHash) { + throw new Error(`Native superseded transition changed before removal for ${journal.change}`); + } + await fs25.rm(transitionFile); + await hooks?.afterTransitionSuperseded?.(journal); + } +} +async function continueRunRetreat(paths, journal, hooks) { + if (!journal.runRetreat) return; + const changeDir = nativeChangeDir(paths, journal.change); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) { + throw new Error(`Native schema migration Run state disappeared for ${journal.change}`); + } + if (!sameRunState(currentRun, journal.runRetreat.nextRun)) { + if (!sameRunState(currentRun, journal.runRetreat.previousRun)) { + throw new Error(`Native schema migration Run source changed for ${journal.change}`); + } + await writeNativeRunState(changeDir, journal.runRetreat.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + let trajectory = await readNativeTrajectory(changeDir, journal.runRetreat.nextRun.trajectoryRef); + let event = trajectory.find( + (item) => item.type === "state_migrated" && item.data.migrationId === journal.id + ); + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir, + run: journal.runRetreat.nextRun, + type: "state_migrated", + data: { + ...journal.runRetreat.eventData, + migrationId: journal.id, + evidenceHash: journal.runRetreat.evidenceHash + }, + now: new Date(journal.createdAt) + }); + trajectory = [...trajectory, event]; + } + await hooks?.afterTrajectoryWritten?.(journal); + await writeNativeCheckpoint2({ + changeDir, + run: journal.runRetreat.nextRun, + trajectoryOffset: trajectory.length, + evidenceHash: journal.runRetreat.evidenceHash, + now: new Date(journal.createdAt) + }); + await hooks?.afterCheckpointWritten?.(journal); +} +async function continueNativeSchemaMigrationLocked(paths, name, hooks) { + const journal = await inspectPendingNativeSchemaMigration(paths, name); + if (!journal) return null; + if (await hasPendingNativeCheckpointRecovery(paths, name)) { + throw new Error( + `Native change ${name} has a pending progress checkpoint; recover it with its v2 runtime before schema migration` + ); + } + const changeFile = path38.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + const actualHash = await sha256File(changeFile); + await assertSupersedeSourceBeforeMutation(paths, journal, actualHash === journal.targetHash); + await ensureMigrationBaseline(paths, name, journal.createdAt); + if (actualHash !== journal.targetHash) { + if (actualHash !== journal.sourceHash) { + throw new Error( + `Native schema migration source changed for ${name}: expected ${journal.sourceHash}, actual ${actualHash}` + ); + } + await atomicWriteText(changeFile, (0, import_yaml4.stringify)(migrationStateDocument(journal.nextState))); + await hooks?.afterStateWritten?.(journal); + } + if (journal.transition) { + const transitionFile = nativeTransitionJournalFile(paths, name); + const actualTransitionHash = await sha256File(transitionFile); + if (actualTransitionHash !== journal.transition.targetHash) { + if (actualTransitionHash !== journal.transition.sourceHash) { + throw new Error( + `Native transition migration source changed for ${name}: expected ${journal.transition.sourceHash}, actual ${actualTransitionHash}` + ); + } + await atomicWriteJson(transitionFile, journal.transition.nextJournal); + await hooks?.afterTransitionWritten?.(journal); + } + } + await continueTransitionSupersede(paths, journal, hooks); + await continueRunRetreat(paths, journal, hooks); + await fs25.rm(nativeSchemaMigrationJournalFile(paths, name), { force: true }); + return journal.nextState; +} +async function stableEvidenceRetreat(options) { + const changeDir = nativeChangeDir(options.paths, options.state.name); + const run = await readNativeRunState(changeDir); + if (!run || run.runId !== options.state.run_id || run.currentStep !== options.state.phase || run.currentStep !== "verify" && run.currentStep !== "archive" || run.pending !== null) { + throw new Error( + `Native v2 ${options.state.phase} change ${options.state.name} has no consistent Run state for safe Build retreat` + ); + } + const nextRun = { + ...run, + currentStep: "build", + iteration: run.iteration + 1, + pending: null, + status: "running" + }; + const evidenceHash = sha256Text( + `schema-v3-evidence-retreat:${options.state.name}:${options.state.revision}:${options.migrationId}` + ); + return { + previousRun: run, + nextRun, + evidenceHash, + eventData: { + fromSchema: NATIVE_V2_CHANGE_SCHEMA, + toSchema: NATIVE_CHANGE_SCHEMA, + previousPhase: options.state.phase, + nextPhase: "build", + reason: "implementation-scope-required" + } + }; +} +async function pendingEvidenceTransitionSupersede(options) { + const { journal, state } = options; + const requiresV3Evidence = journal.previousState.phase === "build" && journal.nextState.phase === "verify" || journal.previousState.phase === "verify" && (journal.nextState.phase === "build" || journal.nextState.phase === "archive"); + if (!requiresV3Evidence) { + throw new Error("Only a pending v2 evidence-bearing transition can be superseded"); + } + const stateIsPrevious = sameV2State(state, journal.previousState); + const stateIsNext = sameV2State(state, journal.nextState); + if (!stateIsPrevious && !stateIsNext) { + throw new Error(`Native change ${state.name} does not match its v2 evidence transition`); + } + const changeDir = nativeChangeDir(options.paths, state.name); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) throw new Error(`Native v2 transition Run is missing for ${state.name}`); + const runIsPrevious = journal.previousRun !== null && sameRunState(currentRun, journal.previousRun); + const runIsNext = sameRunState(currentRun, journal.nextRun); + if (!runIsPrevious && !runIsNext || stateIsNext && !runIsNext) { + throw new Error(`Native v2 transition Run does not match its durable state for ${state.name}`); + } + const trajectory = await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef); + const collisions = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.transitionId === journal.id); + if (collisions.length > 1) { + throw new Error(`Native v2 evidence transition ${journal.id} has duplicate trajectory events`); + } + const durable = collisions[0]; + if (durable) { + const expected = { + sequence: durable.index + 1, + timestamp: journal.createdAt, + type: "state_transitioned", + runId: journal.nextRun.runId, + data: { ...journal.eventData, transitionId: journal.id } + }; + if (!isDeepStrictEqual3(durable.event, expected) || !stateIsNext || !runIsNext) { + throw new Error( + `Native v2 evidence transition ${journal.id} trajectory event does not match its journal` + ); + } + } + const nextState = { + ...upgradeV2StateToV3(state), + revision: state.revision + (state.phase === "build" ? 0 : 1), + phase: "build", + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: false + }; + const previousRun = currentRun; + const nextRun = currentRun.currentStep !== "build" ? { + ...currentRun, + currentStep: "build", + iteration: currentRun.iteration + 1, + pending: null, + status: "running" + } : currentRun; + const evidenceHash = sha256Text( + JSON.stringify({ + operation: "supersede-v2-evidence-transition", + change: state.name, + transitionId: journal.id, + migrationId: options.migrationId, + previousPhase: state.phase, + nextPhase: "build", + reason: "implementation-scope-required", + previousIteration: currentRun.iteration, + nextRevision: nextState.revision + }) + ); + return { + nextState, + plan: { + sourceHash: await sha256File(nativeTransitionJournalFile(options.paths, state.name)), + transitionId: journal.id, + previousRun, + nextRun, + evidenceHash, + eventData: { + fromSchema: NATIVE_V2_CHANGE_SCHEMA, + toSchema: NATIVE_CHANGE_SCHEMA, + previousPhase: state.phase, + nextPhase: "build", + reason: "implementation-scope-required", + supersededTransitionId: journal.id + } + } + }; +} +async function prepareNextMigration(options) { + let nextState; + let transition; + let transitionSupersede; + let runRetreat; + if (options.state.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + nextState = upgradeV1StateToV2(options.state, 1); + if (options.pendingTransition) { + if (options.pendingTransition.journal.schema !== NATIVE_LEGACY_TRANSITION_SCHEMA) { + throw new Error("Native v1 change has a transition from another schema generation"); + } + const nextJournal = upgradeV1TransitionToV2(options.pendingTransition.journal); + if (sameV1State(options.state, options.pendingTransition.journal.previousState)) { + nextState = nextJournal.previousState; + } else if (sameV1State(options.state, options.pendingTransition.journal.nextState)) { + nextState = nextJournal.nextState; + } else { + throw new Error( + `Native change ${options.name} does not match either state in its v1 transition journal` + ); + } + const transitionFile = nativeTransitionJournalFile(options.paths, options.name); + transition = { + sourceHash: await sha256File(transitionFile), + targetHash: sha256Text(transitionContent(nextJournal)), + nextJournal + }; + } + } else { + if (options.pendingTransition) { + if (options.pendingTransition.journal.schema !== NATIVE_V2_TRANSITION_SCHEMA) { + throw new Error("Native v2 change has a transition from another schema generation"); + } + const v2Journal = options.pendingTransition.journal; + const requiresV3Evidence = v2Journal.previousState.phase === "build" && v2Journal.nextState.phase === "verify" || v2Journal.previousState.phase === "verify" && (v2Journal.nextState.phase === "build" || v2Journal.nextState.phase === "archive"); + if (requiresV3Evidence) { + const supersede = await pendingEvidenceTransitionSupersede({ + paths: options.paths, + state: options.state, + journal: v2Journal, + migrationId: options.id + }); + nextState = supersede.nextState; + transitionSupersede = supersede.plan; + } else { + const nextJournal = upgradeV2TransitionToV3(v2Journal); + if (sameV2State(options.state, options.pendingTransition.journal.previousState)) { + nextState = nextJournal.previousState; + } else if (sameV2State(options.state, options.pendingTransition.journal.nextState)) { + nextState = nextJournal.nextState; + } else { + throw new Error( + `Native change ${options.name} does not match either state in its v2 transition journal` + ); + } + const transitionFile = nativeTransitionJournalFile(options.paths, options.name); + transition = { + sourceHash: await sha256File(transitionFile), + targetHash: sha256Text(transitionContent(nextJournal)), + nextJournal + }; + } + } else { + nextState = upgradeV2StateToV3(options.state, { + retreatEvidencePhase: true, + incrementRetreatRevision: true + }); + if (options.state.phase === "verify" || options.state.phase === "archive") { + runRetreat = await stableEvidenceRetreat({ + paths: options.paths, + state: options.state, + migrationId: options.id + }); + } + } + } + const changeFile = path38.join( + nativeChangeDir(options.paths, options.name), + NATIVE_CHANGE_STATE_FILE + ); + const targetContent = (0, import_yaml4.stringify)(migrationStateDocument(nextState)); + return { + schema: "comet.native.schema-migration.v1", + id: options.id, + change: options.name, + fromSchema: options.state.schema, + toSchema: options.state.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? NATIVE_V2_CHANGE_SCHEMA : NATIVE_CHANGE_SCHEMA, + sourceHash: await sha256File(changeFile), + targetHash: sha256Text(targetContent), + createdAt: options.now.toISOString(), + nextState, + ...transition ? { transition } : {}, + ...transitionSupersede ? { transitionSupersede } : {}, + ...runRetreat ? { runRetreat } : {} + }; +} +async function migrateNativeChange(options) { + return withNativeMutationLock( + options.paths, + `migrate schema for ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `migrate schema for ${options.name}`, + async () => { + for (let step = 0; step < 3; step += 1) { + const continued = await continueNativeSchemaMigrationLocked( + options.paths, + options.name, + options.hooks + ); + if (continued?.schema === NATIVE_CHANGE_SCHEMA) return continued; + const inspection = await inspectNativeChange(options.paths, options.name); + if (inspection.status === "current" && inspection.state) { + return inspection.state; + } + if (inspection.status !== "migration-required" || !inspection.state) { + throw new Error( + inspection.message ?? `Native change ${options.name} cannot be migrated` + ); + } + if (await hasPendingNativeCheckpointRecovery(options.paths, options.name)) { + throw new Error( + `Native change ${options.name} has a pending progress checkpoint; recover it with its v2 runtime before schema migration` + ); + } + const state = inspection.state; + if (state.schema !== NATIVE_LEGACY_CHANGE_SCHEMA && state.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Native change ${options.name} has no supported migration route`); + } + const pendingTransition = await inspectPendingNativeTransitionSchema( + options.paths, + options.name + ); + if (pendingTransition?.status === "current") { + throw new Error( + `Native change ${options.name} has a current-schema pending transition; recover it before schema migration` + ); + } + const journal = await prepareNextMigration({ + paths: options.paths, + name: options.name, + state, + pendingTransition, + now: options.now ?? /* @__PURE__ */ new Date(), + id: options.id?.() ?? randomUUID7() + }); + const journalFile = nativeSchemaMigrationJournalFile(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, journalFile); + await atomicWriteJson(journalFile, journal); + await options.hooks?.afterPrepared?.(journal); + } + throw new Error( + `Native change ${options.name} exceeded the supported schema migration path` + ); + } + ) + ); +} + +// domains/comet-native/native-evidence-retention.ts +var NATIVE_EVIDENCE_RETENTION_POLICY = Object.freeze({ + minimumAgeMs: 30 * 24 * 60 * 60 * 1e3, + keepLatestUnreferencedPerKind: 32, + maxDocuments: 4096, + maxScannedBytes: 256 * 1024 * 1024, + maxReportedRefs: 8, + maxReportMessageBytes: 4096 +}); +var MAX_NATIVE_CHECK_RECEIPT_BYTES2 = 512 * 1024; +var HASH_FILE_PATTERN = /^([a-f0-9]{64})\.json$/u; +var CLEANUP_QUARANTINE_PATTERN = /^\.([a-f0-9]{64}\.json)\.([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})\.gc$/u; +var MANAGED_REF_PATTERN = /^runtime\/evidence\/(snapshots|scopes|allowances|verifications|reports|check-receipts)\/[a-f0-9]{64}\.json$/u; +var MANAGED_KINDS = [ + "snapshots", + "scopes", + "allowances", + "verifications", + "reports", + "check-receipts" +]; +function sameObjectIdentity(left, right) { + return sameFileObject( + { ...left, birthtime: left.birthtimeMs }, + { + ...right, + birthtime: right.birthtimeMs + } + ); +} +function sameIdentity(left, right) { + return sameObjectIdentity(left, right) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; +} +function sameContentVersionAfterLinkOrRename(left, right) { + return sameObjectIdentity(left, right) && left.birthtimeMs === right.birthtimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; +} +function fileIdentity2(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function compareText8(left, right) { + return left < right ? -1 : left > right ? 1 : 0; +} +function portableProjectRef(paths, target) { + const relative = path39.relative(paths.projectRoot, target); + if (relative.length === 0 || path39.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path39.sep}`)) { + throw new Error("Native evidence retention finding path is outside the project"); + } + return relative.split(path39.sep).join("/"); +} +function evidenceRootPath(paths, name) { + return path39.join(nativeChangeDir(paths, name), "runtime", "evidence"); +} +function evidenceRootRef(paths, name) { + return portableProjectRef(paths, evidenceRootPath(paths, name)); +} +async function captureDirectoryChain6(managedRoot, directory) { + const root = path39.resolve(managedRoot); + const target = path39.resolve(directory); + if (!isInsidePath(root, target)) { + throw new Error("Native evidence retention path escapes its managed change"); + } + const segments = path39.relative(root, target).split(path39.sep).filter(Boolean); + const chain = []; + let cursor = root; + for (const segment of ["", ...segments]) { + if (segment) cursor = path39.join(cursor, segment); + const stat = await fs26.lstat(cursor); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native evidence retention parent must be a real directory: ${cursor}`); + } + const realPath = await fs26.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new Error(`Native evidence retention parent resolves outside its change: ${cursor}`); + } + chain.push({ path: cursor, realPath, ...fileIdentity2(stat) }); + } + return chain; +} +async function verifyDirectoryChain7(chain) { + for (const identity of chain) { + const stat = await fs26.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameObjectIdentity(identity, stat) || await fs26.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native evidence retention parent changed: ${identity.path}`); + } + } +} +function refForKind(kind, hash6) { + return kind === "check-receipts" ? `runtime/evidence/check-receipts/${hash6}.json` : nativeEvidenceRef(kind, hash6); +} +function managedDependency(value) { + return value !== null && MANAGED_REF_PATTERN.test(value) ? [value] : []; +} +function parseDocument4(kind, hash6, value, expectedChange) { + if (kind === "snapshots") { + return { + canonical: parseNativeSnapshotProjection(value, hash6), + dependencies: [] + }; + } + if (kind === "scopes") { + const scope = parseNativeImplementationScope(value); + if (scope.scopeHash !== hash6) throw new Error("Native scope filename does not match its hash"); + return { + canonical: scope, + dependencies: [scope.baselineProjectionRef, scope.currentProjectionRef] + }; + } + if (kind === "allowances") { + const allowance = parseNativePartialAllowance(value); + if (allowance.change !== expectedChange || allowance.allowanceHash !== hash6) { + throw new Error("Native allowance filename/change does not match its content"); + } + return { + canonical: allowance, + dependencies: [nativeEvidenceRef("scopes", allowance.scopeHash)] + }; + } + if (kind === "verifications") { + const evidence = parseNativeVerificationEvidenceEnvelope(value); + if (evidence.change !== expectedChange || evidence.envelopeHash !== hash6) { + throw new Error("Native verification filename/change does not match its content"); + } + const traceDependencies = evidence.acceptanceTrace.entries.flatMap( + (entry2) => entry2.evidenceRefs.flatMap(managedDependency) + ); + return { + canonical: evidence, + dependencies: [ + evidence.implementationScopeRef, + nativeReportEvidenceRef(evidence.reportHash), + ...managedDependency(evidence.partialAllowanceRef), + ...managedDependency(evidence.receiptRef), + ...traceDependencies + ] + }; + } + if (kind === "reports") { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native report evidence must be an object"); + } + const report = value; + if (Object.keys(report).sort().join(",") !== "content,reportHash,schema" || report.schema !== "comet.native.verification-report.v1" || report.reportHash !== hash6 || typeof report.content !== "string" || createHash13("sha256").update(Buffer.from(report.content, "utf8")).digest("hex") !== hash6) { + throw new Error("Native report evidence filename/hash does not match its content"); + } + return { canonical: report, dependencies: [] }; + } + const receipt = parseNativeCheckReceipt(value); + if (receipt.change !== expectedChange || receipt.receiptHash !== hash6) { + throw new Error("Native check receipt filename/change does not match its content"); + } + return { canonical: receipt, dependencies: [] }; +} +async function readCanonicalDocument(options) { + const before = await fs26.lstat(options.file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native evidence entry is not a regular file: ${options.ref}`); + } + const maximumBytes = options.kind === "check-receipts" ? MAX_NATIVE_CHECK_RECEIPT_BYTES2 : MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES; + if (before.size > maximumBytes) { + throw new Error(`Native evidence entry exceeds its byte budget: ${options.ref}`); + } + const beforeRealPath = await fs26.realpath(options.file); + if (!isInsidePath(options.directoryChain[0].realPath, beforeRealPath)) { + throw new Error(`Native evidence entry resolves outside its change: ${options.ref}`); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants5.O_RDONLY | fsConstants5.O_NOFOLLOW | fsConstants5.O_NONBLOCK; + const handle = await fs26.open(options.file, openFlags).catch((error) => { + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new Error(`Native evidence entry became unsafe while opening: ${options.ref}`); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs26.lstat(options.file), + fs26.realpath(options.file) + ]); + await verifyDirectoryChain7(options.directoryChain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameIdentity(fileIdentity2(before), opened) || !sameIdentity(fileIdentity2(opened), pathAfterOpen)) { + throw new Error(`Native evidence entry changed while opening: ${options.ref}`); + } + const chunks = []; + const buffer = Buffer.allocUnsafe(64 * 1024); + let total = 0; + while (true) { + const remaining = maximumBytes + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > maximumBytes) { + throw new Error(`Native evidence entry exceeds its byte budget: ${options.ref}`); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs26.lstat(options.file), + fs26.realpath(options.file) + ]); + await verifyDirectoryChain7(options.directoryChain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameIdentity(fileIdentity2(opened), afterHandle) || !sameIdentity(fileIdentity2(opened), afterPath)) { + throw new Error(`Native evidence entry changed while reading: ${options.ref}`); + } + const text = Buffer.concat(chunks, total).toString("utf8"); + let json; + try { + json = JSON.parse(text); + } catch (error) { + throw new Error(`Native evidence entry is not valid JSON: ${options.ref}`, { + cause: error + }); + } + const parsed = parseDocument4(options.kind, options.hash, json, options.name); + if (text !== JSON.stringify(parsed.canonical, null, 2) + "\n") { + throw new Error(`Native evidence entry is not canonically serialized: ${options.ref}`); + } + return { + ref: options.ref, + kind: options.kind, + change: options.name, + changeRoot: options.changeRoot, + file: options.file, + size: total, + mtimeMs: afterHandle.mtimeMs, + identity: fileIdentity2(afterHandle), + directoryChain: options.directoryChain, + dependencies: [...new Set(parsed.dependencies)].sort(compareText8) + }; + } finally { + await handle.close(); + } +} +async function pathIsMissing(file) { + try { + await fs26.lstat(file); + return false; + } catch (error) { + if (error.code === "ENOENT") return true; + throw error; + } +} +async function inspectCleanupQuarantines(options) { + const changeRoot = nativeChangeDir(options.paths, options.name); + const evidenceRoot = evidenceRootPath(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, evidenceRoot); + let rootEntries; + try { + const rootChain = await captureDirectoryChain6(changeRoot, evidenceRoot); + rootEntries = await fs26.readdir(evidenceRoot, { withFileTypes: true }); + await verifyDirectoryChain7(rootChain); + } catch (error) { + if (error.code === "ENOENT") { + return { findings: [], pending: false }; + } + throw error; + } + const recoveries = []; + for (const kind of MANAGED_KINDS) { + if (!rootEntries.some((entry2) => entry2.name === kind)) continue; + const directory = path39.join(evidenceRoot, kind); + const directoryChain = await captureDirectoryChain6(changeRoot, directory); + const entries = (await fs26.readdir(directory, { withFileTypes: true })).sort( + (left, right) => compareText8(left.name, right.name) + ); + const byOriginal = /* @__PURE__ */ new Map(); + for (const entry2 of entries) { + const match = CLEANUP_QUARANTINE_PATTERN.exec(entry2.name); + if (!match) continue; + const grouped = byOriginal.get(match[1]) ?? []; + grouped.push(entry2); + byOriginal.set(match[1], grouped); + } + for (const [originalName, entriesForOriginal] of [...byOriginal.entries()].sort( + ([left], [right]) => compareText8(left, right) + )) { + if (entriesForOriginal.length !== 1) { + throw new Error( + `Native evidence cleanup has multiple quarantines for ${kind}/${originalName}` + ); + } + const entry2 = entriesForOriginal[0]; + if (!entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error( + `Native evidence cleanup quarantine is not a regular file: ${kind}/${entry2.name}` + ); + } + const original = path39.join(directory, originalName); + const hash6 = HASH_FILE_PATTERN.exec(originalName)?.[1]; + if (!hash6) + throw new Error(`Native evidence cleanup quarantine name is invalid: ${entry2.name}`); + const ref = refForKind(kind, hash6); + const quarantine = path39.join(directory, entry2.name); + const document = await readCanonicalDocument({ + changeRoot, + file: quarantine, + ref, + kind, + hash: hash6, + name: options.name, + directoryChain + }); + const originalMissing = await pathIsMissing(original); + const originalDocument = originalMissing ? null : await readCanonicalDocument({ + changeRoot, + file: original, + ref, + kind, + hash: hash6, + name: options.name, + directoryChain + }); + recoveries.push({ + ref, + original, + quarantine, + document, + action: originalMissing ? "restore-original" : "finish-linked-recovery", + originalDocument + }); + } + } + if (recoveries.length === 0) return { findings: [], pending: false }; + const refs = recoveries.map((recovery) => recovery.ref).sort(compareText8); + if (!options.repair) { + return { + pending: true, + findings: [ + { + severity: "warning", + code: "evidence-retention-recovery-required", + message: boundedMessage( + `Native evidence cleanup has ${refs.length} safely recoverable quarantined document(s) for ${options.name}: ${boundedRefs(refs)}` + ), + path: evidenceRootRef(options.paths, options.name) + } + ] + }; + } + for (const recovery of recoveries.sort((left, right) => compareText8(left.ref, right.ref))) { + await verifyDirectoryChain7(recovery.document.directoryChain); + if (recovery.action === "finish-linked-recovery") { + const original = await readCanonicalDocument({ + changeRoot, + file: recovery.original, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + const quarantine = await readCanonicalDocument({ + changeRoot, + file: recovery.quarantine, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + if (!recovery.originalDocument || !sameIdentity(recovery.originalDocument.identity, original.identity) || !sameIdentity(recovery.document.identity, quarantine.identity)) { + throw new Error( + `Native evidence cleanup original or quarantine changed during recovery: ${recovery.ref}` + ); + } + await fs26.rm(recovery.quarantine); + await verifyDirectoryChain7(recovery.document.directoryChain); + continue; + } + if (!await pathIsMissing(recovery.original)) { + throw new Error(`Native evidence cleanup original appeared during recovery: ${recovery.ref}`); + } + const quarantineStat = await fs26.lstat(recovery.quarantine); + if (!quarantineStat.isFile() || quarantineStat.isSymbolicLink() || !sameIdentity(recovery.document.identity, quarantineStat)) { + throw new Error( + `Native evidence cleanup quarantine changed during recovery: ${recovery.ref}` + ); + } + await fs26.link(recovery.quarantine, recovery.original); + await options.hooks.afterRecoveryLink?.({ + ref: recovery.ref, + original: recovery.original, + quarantine: recovery.quarantine + }); + const restored = await readCanonicalDocument({ + changeRoot, + file: recovery.original, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + const linkedQuarantine = await readCanonicalDocument({ + changeRoot, + file: recovery.quarantine, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + if (!sameContentVersionAfterLinkOrRename(recovery.document.identity, restored.identity) || !sameIdentity(restored.identity, linkedQuarantine.identity)) { + throw new Error(`Native evidence cleanup recovery identity changed: ${recovery.ref}`); + } + await fs26.rm(recovery.quarantine); + await verifyDirectoryChain7(recovery.document.directoryChain); + } + return { + pending: false, + findings: [ + { + severity: "info", + code: "evidence-retention-cleanup-recovered", + message: boundedMessage( + `Recovered ${refs.length} interrupted Native evidence cleanup document(s) for ${options.name}: ${boundedRefs(refs)}` + ), + path: evidenceRootRef(options.paths, options.name) + } + ] + }; +} +async function scanEvidenceStore(paths, name) { + const changeRoot = nativeChangeDir(paths, name); + const evidenceRoot = path39.join(changeRoot, "runtime", "evidence"); + await resolveContainedNativePath(paths.nativeRoot, evidenceRoot); + let rootEntries; + try { + const rootChain = await captureDirectoryChain6(changeRoot, evidenceRoot); + rootEntries = await fs26.readdir(evidenceRoot, { withFileTypes: true }); + await verifyDirectoryChain7(rootChain); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const unknownRootEntries = rootEntries.filter( + (entry2) => !MANAGED_KINDS.includes(entry2.name) || !entry2.isDirectory() || entry2.isSymbolicLink() + ).map((entry2) => entry2.name).sort(compareText8); + if (unknownRootEntries.length > 0) { + throw new Error( + `Native evidence root has unknown or special entries: ${boundedRefs(unknownRootEntries)}` + ); + } + const documents = []; + let scannedBytes = 0; + for (const kind of MANAGED_KINDS) { + if (!rootEntries.some((entry2) => entry2.name === kind)) continue; + const directory = path39.join(evidenceRoot, kind); + const directoryChain = await captureDirectoryChain6(changeRoot, directory); + const entries = (await fs26.readdir(directory, { withFileTypes: true })).sort( + (left, right) => compareText8(left.name, right.name) + ); + await verifyDirectoryChain7(directoryChain); + for (const entry2 of entries) { + if (documents.length >= NATIVE_EVIDENCE_RETENTION_POLICY.maxDocuments) { + throw new Error( + `Native evidence store exceeds ${NATIVE_EVIDENCE_RETENTION_POLICY.maxDocuments} documents` + ); + } + const match = HASH_FILE_PATTERN.exec(entry2.name); + if (!match || !entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error( + `Native evidence kind ${kind} has an unknown or special entry: ${entry2.name}` + ); + } + const ref = refForKind(kind, match[1]); + const file = path39.join(directory, entry2.name); + await resolveContainedNativePath(paths.nativeRoot, file); + const document = await readCanonicalDocument({ + changeRoot, + file, + ref, + kind, + hash: match[1], + name, + directoryChain + }); + scannedBytes += document.size; + if (scannedBytes > NATIVE_EVIDENCE_RETENTION_POLICY.maxScannedBytes) { + throw new Error( + `Native evidence store exceeds ${NATIVE_EVIDENCE_RETENTION_POLICY.maxScannedBytes} scanned bytes` + ); + } + documents.push(document); + } + await verifyDirectoryChain7(directoryChain); + } + return documents; +} +function closeDependencies(seeds, documents) { + const retained = /* @__PURE__ */ new Set(); + const pending = [...seeds].sort(compareText8).reverse(); + while (pending.length > 0) { + const ref = pending.pop(); + if (retained.has(ref)) continue; + const document = documents.get(ref); + if (!document) throw new Error(`Native evidence dependency is missing: ${ref}`); + retained.add(ref); + for (const dependency of [...document.dependencies].sort(compareText8).reverse()) { + if (!retained.has(dependency)) pending.push(dependency); + } + } + return retained; +} +function dependentsBeforeDependencies(candidates) { + const byRef = new Map(candidates.map((candidate) => [candidate.ref, candidate])); + const dependencyCounts = new Map(candidates.map((candidate) => [candidate.ref, 0])); + for (const candidate of candidates) { + for (const dependency of candidate.dependencies) { + if (byRef.has(dependency)) { + dependencyCounts.set(dependency, (dependencyCounts.get(dependency) ?? 0) + 1); + } + } + } + const ready = [...dependencyCounts].filter(([, count]) => count === 0).map(([ref]) => ref).sort(compareText8); + const ordered = []; + while (ready.length > 0) { + const ref = ready.shift(); + const candidate = byRef.get(ref); + ordered.push(candidate); + for (const dependency of candidate.dependencies) { + const count = dependencyCounts.get(dependency); + if (count === void 0) continue; + const next = count - 1; + dependencyCounts.set(dependency, next); + if (next === 0) { + ready.push(dependency); + ready.sort(compareText8); + } + } + } + if (ordered.length !== candidates.length) { + throw new Error("Native evidence candidate dependency graph contains a cycle"); + } + return ordered; +} +function planRetention(documents, rootRefs, nowMs) { + const byRef = new Map(documents.map((document) => [document.ref, document])); + if (byRef.size !== documents.length) throw new Error("Native evidence store has duplicate refs"); + for (const document of documents) { + for (const dependency of document.dependencies) { + if (!byRef.has(dependency)) { + throw new Error(`Native evidence dependency is missing: ${dependency}`); + } + } + } + const rootClosure = closeDependencies(rootRefs, byRef); + const retainedSeeds = new Set(rootClosure); + for (const document of documents) { + if (nowMs - document.mtimeMs < NATIVE_EVIDENCE_RETENTION_POLICY.minimumAgeMs) { + retainedSeeds.add(document.ref); + } + } + for (const kind of MANAGED_KINDS) { + const newestUnreferenced = documents.filter((document) => document.kind === kind && !rootClosure.has(document.ref)).sort((left, right) => right.mtimeMs - left.mtimeMs || compareText8(left.ref, right.ref)).slice(0, NATIVE_EVIDENCE_RETENTION_POLICY.keepLatestUnreferencedPerKind); + for (const document of newestUnreferenced) retainedSeeds.add(document.ref); + } + const retained = closeDependencies(retainedSeeds, byRef); + const candidates = documents.filter( + (document) => !retained.has(document.ref) && nowMs - document.mtimeMs >= NATIVE_EVIDENCE_RETENTION_POLICY.minimumAgeMs + ).sort((left, right) => compareText8(left.ref, right.ref)); + return { + candidates: dependentsBeforeDependencies(candidates), + candidateBytes: candidates.reduce((total, candidate) => total + candidate.size, 0) + }; +} +function boundedRefs(refs) { + const shown = refs.slice(0, NATIVE_EVIDENCE_RETENTION_POLICY.maxReportedRefs); + const omitted = refs.length - shown.length; + return `${shown.join(", ")}${omitted > 0 ? `, ... (${omitted} more)` : ""}`; +} +function boundedMessage(message) { + const maximum = NATIVE_EVIDENCE_RETENTION_POLICY.maxReportMessageBytes; + if (Buffer.byteLength(message, "utf8") <= maximum) return message; + let result2 = message; + while (result2.length > 0 && Buffer.byteLength(`${result2}...`, "utf8") > maximum) { + result2 = result2.slice(0, -1); + } + return `${result2}...`; +} +function summaryFinding(name, plan, repaired, evidenceRoot) { + const refs = plan.candidates.map((candidate) => candidate.ref).sort(compareText8); + return { + severity: "info", + code: repaired ? "evidence-retention-cleaned" : "evidence-retention-candidates", + message: boundedMessage( + `${repaired ? "Removed" : "Found"} ${plan.candidates.length} old unreferenced Native evidence document(s) (${plan.candidateBytes} bytes) for ${name}: ${boundedRefs(refs)}` + ), + path: evidenceRoot + }; +} +async function restoreQuarantineIfSafe(quarantine, original) { + try { + await fs26.lstat(original); + return; + } catch (error) { + if (error.code !== "ENOENT") return; + } + try { + await fs26.rename(quarantine, original); + } catch { + } +} +async function deleteCandidate(candidate, hooks) { + await hooks.beforeDelete?.({ ref: candidate.ref, file: candidate.file }); + await verifyDirectoryChain7(candidate.directoryChain); + const beforeDocument = await readCanonicalDocument({ + changeRoot: candidate.changeRoot, + file: candidate.file, + ref: candidate.ref, + kind: candidate.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(candidate.file))[1], + name: candidate.change, + directoryChain: candidate.directoryChain + }); + const before = await fs26.lstat(candidate.file); + const beforeRealPath = await fs26.realpath(candidate.file); + if (!before.isFile() || before.isSymbolicLink() || !sameIdentity(candidate.identity, before) || !sameIdentity(candidate.identity, beforeDocument.identity) || !isInsidePath(candidate.directoryChain[0].realPath, beforeRealPath)) { + throw new Error(`Native evidence candidate changed before cleanup: ${candidate.ref}`); + } + const quarantine = path39.join( + path39.dirname(candidate.file), + `.${path39.basename(candidate.file)}.${randomUUID8()}.gc` + ); + await fs26.rename(candidate.file, quarantine); + try { + const [moved, movedRealPath] = await Promise.all([ + fs26.lstat(quarantine), + fs26.realpath(quarantine) + ]); + await verifyDirectoryChain7(candidate.directoryChain); + if (!moved.isFile() || moved.isSymbolicLink() || !sameContentVersionAfterLinkOrRename(candidate.identity, moved) || !isInsidePath(candidate.directoryChain[0].realPath, movedRealPath)) { + await restoreQuarantineIfSafe(quarantine, candidate.file); + throw new Error(`Native evidence candidate changed during cleanup: ${candidate.ref}`); + } + await fs26.rm(quarantine); + await verifyDirectoryChain7(candidate.directoryChain); + } catch (error) { + await restoreQuarantineIfSafe(quarantine, candidate.file); + throw error; + } +} +async function hasPendingRelocationOrArchive(paths) { + const config = await readProjectConfig(paths.projectRoot); + if (config?.native.pending_root_move) return true; + let entries; + try { + entries = await fs26.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) return true; + try { + const transaction = await readNativeTransaction(paths, entry2.name); + if (transaction.status !== "committed" && transaction.status !== "rolled-back") return true; + } catch { + return true; + } + } + return false; +} +async function changeNames(paths, requested) { + if (requested) return [requested]; + let entries; + try { + entries = await fs26.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + return entries.filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(compareText8); +} +async function retentionDeferred(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status !== "current" || !inspection.state || inspection.state.archived) + return true; + const [transition, migration, checkpoint] = await Promise.all([ + inspectPendingNativeTransition(paths, name), + inspectPendingNativeSchemaMigration(paths, name), + readNativeCheckpointJournal(paths, name) + ]); + return transition !== null || migration !== null || checkpoint !== null; +} +async function collectChangeRetention(options) { + if (await retentionDeferred(options.paths, options.name)) return []; + const inspection = await inspectNativeChange(options.paths, options.name); + if (inspection.status !== "current" || !inspection.state) return []; + if (!("implementation_scope" in inspection.state)) return []; + const cleanupRecovery = await inspectCleanupQuarantines({ + paths: options.paths, + name: options.name, + repair: options.repair, + hooks: options.hooks + }); + if (cleanupRecovery.pending) return cleanupRecovery.findings; + const rootRefs = []; + for (const value of [ + inspection.state.implementation_scope, + inspection.state.partial_allowance, + inspection.state.verification_evidence + ]) { + if (value !== null) rootRefs.push(value); + } + const documents = await scanEvidenceStore(options.paths, options.name); + const plan = planRetention(documents, rootRefs, options.nowMs); + if (plan.candidates.length === 0) return cleanupRecovery.findings; + const evidenceRef = evidenceRootRef(options.paths, options.name); + if (!options.repair) { + return [...cleanupRecovery.findings, summaryFinding(options.name, plan, false, evidenceRef)]; + } + const deleted = []; + for (const candidate of plan.candidates) { + try { + await deleteCandidate(candidate, options.hooks); + deleted.push(candidate); + } catch (error) { + const deletedBytes = deleted.reduce((total, item) => total + item.size, 0); + return [ + ...cleanupRecovery.findings, + ...deleted.length > 0 ? [ + summaryFinding( + options.name, + { candidates: deleted, candidateBytes: deletedBytes }, + true, + evidenceRef + ) + ] : [], + { + severity: "error", + code: "evidence-retention-cleanup-failed", + message: boundedMessage( + `Native evidence cleanup stopped safely for ${options.name}: ${error.message}` + ), + path: evidenceRef + } + ]; + } + } + return [...cleanupRecovery.findings, summaryFinding(options.name, plan, true, evidenceRef)]; +} +async function inspectNativeEvidenceRetention(options) { + if (options.deferAll || await hasPendingRelocationOrArchive(options.paths)) return []; + const now = options.now ?? /* @__PURE__ */ new Date(); + if (Number.isNaN(now.getTime())) throw new Error("Native evidence retention time is invalid"); + const run = async (repair) => { + const findings = []; + for (const name of await changeNames(options.paths, options.name)) { + try { + findings.push( + ...await collectChangeRetention({ + paths: options.paths, + name, + repair, + nowMs: now.getTime(), + hooks: options.hooks ?? {} + }) + ); + } catch (error) { + findings.push({ + severity: "error", + code: "evidence-retention-unsafe", + message: boundedMessage( + `Native evidence retention refused to clean ${name}: ${error.message}` + ), + path: evidenceRootRef(options.paths, name) + }); + } + } + return findings; + }; + if (!(options.repair ?? false)) return run(false); + const preflight = await run(false); + if (preflight.some((finding) => finding.severity === "error")) return preflight; + if (!preflight.some( + (finding) => finding.code === "evidence-retention-candidates" || finding.code === "evidence-retention-recovery-required" + )) { + return preflight; + } + try { + return await withNativeMutationLock(options.paths, "prune Native evidence", () => run(true)); + } catch (error) { + return [ + { + severity: "info", + code: "evidence-retention-cleanup-deferred", + message: boundedMessage( + `Native evidence cleanup was deferred until the project mutation lock is available: ${error.message}` + ) + } + ]; + } +} + +// domains/comet-native/native-root-move.ts +import { createHash as createHash14, randomUUID as randomUUID9 } from "crypto"; +import { promises as fs27 } from "fs"; +import path40 from "path"; +var NATIVE_ROOT_MOVE_MAX_FILE_BYTES = 64 * 1024 * 1024; +var NATIVE_ROOT_MOVE_MAX_JOURNAL_BYTES = 256 * 1024; +var NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES = 16 * 1024 * 1024; +async function exists3(target) { + try { + await fs27.access(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function assertNoUnfinishedTransactions(paths) { + let entries; + try { + entries = await fs27.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let journal; + try { + journal = await readNativeTransaction(paths, entry2.name); + } catch (error) { + if (error.code === "ENOENT") { + throw new Error( + `Native transaction ${entry2.name} has no journal; run doctor before moving`, + { cause: error } + ); + } + throw error; + } + if (journal.status !== "committed" && journal.status !== "rolled-back") { + throw new Error(`Native transaction ${journal.id} is unfinished; recover it before moving`); + } + } +} +async function assertNoOtherLocks(paths, ownedLock) { + for (const entry2 of await fs27.readdir(paths.locksDir, { withFileTypes: true })) { + const file = path40.join(paths.locksDir, entry2.name); + if (path40.resolve(file) === path40.resolve(ownedLock)) continue; + if (entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error(`Native lock must be diagnosed before moving the root: ${file}`); + } + } +} +async function refreshNativeWorkspaceIdentities(paths) { + let entries; + try { + entries = await fs27.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + for (const entry2 of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let inspection; + try { + inspection = await inspectNativeChange(paths, entry2.name); + } catch { + continue; + } + if (inspection.status !== "current" || !inspection.state || !("revision" in inspection.state)) { + continue; + } + await writeNativeWorkspaceIdentity({ + paths, + name: entry2.name, + revision: inspection.state.revision + }); + } +} +async function walkTree(root, options) { + const treeEntries = []; + async function visit(directory) { + const protectedDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: "Native root tree" + }); + const entries = protectedDirectory.entries; + entries.sort((left, right) => left.name.localeCompare(right.name)); + for (const entry2 of entries) { + await protectedDirectory.verify(); + const target = path40.join(directory, entry2.name); + if (options.excludedFiles?.has(path40.resolve(target))) { + await protectedDirectory.verify(); + continue; + } + const stat = await fs27.lstat(target); + if (entry2.isSymbolicLink() || stat.isSymbolicLink()) { + if (options.rejectSymlinks) throw new Error(`Native root contains a symlink: ${target}`); + await protectedDirectory.verify(); + continue; + } + if (entry2.isDirectory() && stat.isDirectory()) { + treeEntries.push({ + ref: path40.relative(root, target).split(path40.sep).join("/"), + type: "directory" + }); + await visit(target); + } else if (entry2.isFile() && stat.isFile()) { + const snapshot2 = await readNativeProtectedFile({ + root, + file: target, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + label: `Native root file ${path40.relative(root, target)}` + }); + treeEntries.push({ + ref: path40.relative(root, target).split(path40.sep).join("/"), + type: "file", + size: snapshot2.size, + hash: snapshot2.hash + }); + } else { + throw new Error(`Native root contains an unsupported file type: ${target}`); + } + await protectedDirectory.verify(); + } + } + await visit(root); + return treeEntries.sort((left, right) => left.ref.localeCompare(right.ref)); +} +async function copyTree(source, target, excludedFile, targetRoot) { + await ensureNativeProtectedDirectory({ + root: targetRoot, + directory: target, + label: "Native root move staging directory" + }); + async function copyDirectory(from, to) { + const protectedDirectory = await readNativeProtectedDirectory({ + root: source, + directory: from, + label: "Native root move source directory" + }); + const entries = protectedDirectory.entries; + for (const entry2 of entries) { + await protectedDirectory.verify(); + const sourceEntry = path40.join(from, entry2.name); + if (path40.resolve(sourceEntry) === path40.resolve(excludedFile)) { + await protectedDirectory.verify(); + continue; + } + const stat = await fs27.lstat(sourceEntry); + if (entry2.isSymbolicLink() || stat.isSymbolicLink()) { + throw new Error(`Native root contains a symlink: ${sourceEntry}`); + } + const targetEntry = path40.join(to, entry2.name); + if (entry2.isDirectory() && stat.isDirectory()) { + await ensureNativeProtectedDirectory({ + root: targetRoot, + directory: targetEntry, + label: "Native root move staging directory" + }); + await copyDirectory(sourceEntry, targetEntry); + } else if (entry2.isFile() && stat.isFile()) { + await copyNativeProtectedFile({ + sourceRoot: source, + source: sourceEntry, + targetRoot, + target: targetEntry, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + label: `Native root move file ${path40.relative(source, sourceEntry)}`, + exclusive: true, + expectedTargetHash: null + }); + } else { + throw new Error(`Native root contains an unsupported file type: ${sourceEntry}`); + } + await protectedDirectory.verify(); + } + } + await copyDirectory(source, target); +} +async function assertEquivalentTrees(source, target, excludedSourceLock, excludedTargetLock) { + const sourceFiles = await walkTree(source, { + rejectSymlinks: true, + excludedFiles: excludedSourceLock ? /* @__PURE__ */ new Set([path40.resolve(excludedSourceLock)]) : void 0 + }); + const targetFiles = await walkTree(target, { + rejectSymlinks: true, + excludedFiles: excludedTargetLock ? /* @__PURE__ */ new Set([path40.resolve(excludedTargetLock)]) : void 0 + }); + if (JSON.stringify(sourceFiles) !== JSON.stringify(targetFiles)) { + throw new Error( + `Native root copies differ; preserve both trees for manual recovery: ${source} and ${target}` + ); + } +} +function stagingDirectory(targetPaths, id) { + return path40.join(targetPaths.artifactRoot, `.comet-native-move-${id}`); +} +function sourceRemovalDirectory(sourcePaths, id) { + return path40.join(sourcePaths.artifactRoot, `.comet-native-source-${id}.removing`); +} +function stagingRemovalDirectory(staging) { + return `${staging}.removing`; +} +function rollbackRemovalDirectory(target, id) { + return path40.join(path40.dirname(target), `.${path40.basename(target)}.${id}.rollback-removing`); +} +function cleanupManifestFile(paths, id, kind) { + return path40.join(nativeTransactionPaths(paths, id).directory, `root-move-cleanup-${kind}.json`); +} +function cleanupManifestSource(manifest) { + return JSON.stringify(manifest, null, 2) + "\n"; +} +function cleanupManifestHash(source) { + return createHash14("sha256").update(source).digest("hex"); +} +function parseCleanupManifestEntry(value, index) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Native root-move cleanup manifest entry ${index} must be an object`); + } + const entry2 = value; + const keys = Object.keys(entry2).sort(); + const expectedKeys = entry2.type === "file" ? ["hash", "ref", "size", "type"] : ["ref", "type"]; + if (JSON.stringify(keys) !== JSON.stringify(expectedKeys)) { + throw new Error(`Native root-move cleanup manifest entry ${index} has invalid fields`); + } + if (typeof entry2.ref !== "string" || entry2.ref.length === 0 || entry2.ref.includes("\\") || path40.posix.normalize(entry2.ref) !== entry2.ref || entry2.ref.startsWith("/") || entry2.ref.split("/").includes("..") || Buffer.byteLength(entry2.ref, "utf8") > 4096) { + throw new Error(`Native root-move cleanup manifest entry ${index} has an invalid ref`); + } + if (entry2.type === "directory") return { ref: entry2.ref, type: "directory" }; + if (entry2.type !== "file" || typeof entry2.size !== "number" || !Number.isSafeInteger(entry2.size) || entry2.size < 0 || typeof entry2.hash !== "string" || !/^[a-f0-9]{64}$/u.test(entry2.hash)) { + throw new Error(`Native root-move cleanup manifest entry ${index} is invalid`); + } + return { ref: entry2.ref, type: "file", size: entry2.size, hash: entry2.hash }; +} +function parseCleanupManifest(value, id, kind) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native root-move cleanup manifest must be an object"); + } + const manifest = value; + if (JSON.stringify(Object.keys(manifest).sort()) !== JSON.stringify(["entries", "kind", "schema", "transactionId"]) || manifest.schema !== "comet.native.root-move-cleanup.v1" || manifest.transactionId !== id || manifest.kind !== kind || !Array.isArray(manifest.entries)) { + throw new Error("Native root-move cleanup manifest binding is invalid"); + } + const entries = manifest.entries.map(parseCleanupManifestEntry); + for (let index = 0; index < entries.length; index += 1) { + if (index > 0 && entries[index - 1].ref.localeCompare(entries[index].ref) >= 0) { + throw new Error("Native root-move cleanup manifest refs must be unique and sorted"); + } + const parent = path40.posix.dirname(entries[index].ref); + if (parent !== "." && !entries.some((candidate) => candidate.ref === parent && candidate.type === "directory")) { + throw new Error(`Native root-move cleanup manifest parent is missing: ${parent}`); + } + } + return { + schema: "comet.native.root-move-cleanup.v1", + transactionId: id, + kind, + entries + }; +} +async function writeCleanupManifest(options) { + const manifest = { + schema: "comet.native.root-move-cleanup.v1", + transactionId: options.id, + kind: options.kind, + entries: options.entries + }; + const source = cleanupManifestSource(manifest); + if (Buffer.byteLength(source, "utf8") > NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES) { + throw new Error("Native root-move cleanup manifest exceeds its byte budget"); + } + await atomicWriteText(cleanupManifestFile(options.paths, options.id, options.kind), source, { + containedRoot: options.paths.nativeRoot + }); + return cleanupManifestHash(source); +} +async function readCleanupManifest(options) { + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: cleanupManifestFile(options.paths, options.id, options.cleanup.kind), + maxBytes: NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES, + label: `Native root-move cleanup manifest ${options.cleanup.kind}` + }); + if (snapshot2.hash !== options.cleanup.manifestHash) { + throw new Error("Native root-move cleanup manifest hash changed"); + } + return parseCleanupManifest( + JSON.parse(snapshot2.bytes.toString("utf8")), + options.id, + options.cleanup.kind + ); +} +async function assertCleanupManifestMatch(options) { + const current = await walkTree(options.quarantine, { rejectSymlinks: true }); + const expected = new Map(options.manifest.entries.map((entry2) => [entry2.ref, entry2])); + for (const entry2 of current) { + const bound = expected.get(entry2.ref); + if (!bound || JSON.stringify(bound) !== JSON.stringify(entry2)) { + throw new Error( + `Native root-move cleanup quarantine differs from its bound manifest: ${entry2.ref}` + ); + } + } + if (options.exact && current.length !== options.manifest.entries.length) { + throw new Error("Native root-move cleanup quarantine is incomplete before deletion"); + } + return current; +} +function treeEntryDepth(entry2) { + return entry2.ref.split("/").length; +} +async function deleteCleanupManifestSubset(options) { + const current = await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest: options.manifest, + exact: false + }); + const ordered = [...current].sort( + (left, right) => treeEntryDepth(right) - treeEntryDepth(left) || (left.type === right.type ? right.ref.localeCompare(left.ref) : left.type === "file" ? -1 : 1) + ); + let removedCount = 0; + for (const entry2 of ordered) { + const target = path40.join(options.quarantine, ...entry2.ref.split("/")); + if (entry2.type === "file") { + await removeNativeProtectedFile({ + root: options.projectRoot, + file: target, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + expectedHash: entry2.hash, + expectedSize: entry2.size, + label: `Native root-move cleanup file ${entry2.ref}` + }); + } else { + await removeNativeProtectedEmptyDirectory({ + root: options.projectRoot, + directory: target, + label: `Native root-move cleanup directory ${entry2.ref}` + }); + } + removedCount += 1; + await options.hooks?.afterRootMoveCleanupEntryRemoved?.( + options.manifest.kind, + entry2.ref, + removedCount + ); + } + await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest: options.manifest, + exact: false + }); + await removeNativeProtectedEmptyDirectory({ + root: options.projectRoot, + directory: options.quarantine, + label: `Native root-move cleanup quarantine ${options.manifest.kind}` + }); +} +function pendingConfig(config, pending, activeArtifactRoot = config.native.artifact_root) { + return { + ...config, + native: { ...config.native, artifact_root: activeArtifactRoot, pending_root_move: pending } + }; +} +function rootMoveJournal(options) { + return { + schema: "comet.native.transaction.v1", + id: options.id, + kind: "root-move", + status: "prepared", + projectRoot: options.paths.projectRoot, + nativeRoot: options.paths.nativeRoot, + createdAt: options.now.toISOString(), + operations: [] + }; +} +async function readRootMoveJournal(sourcePaths, destinationPaths, stage, id) { + for (const paths of [sourcePaths, destinationPaths]) { + try { + const journal = await readNativeTransaction(paths, id); + if (journal.kind !== "root-move") throw new Error(`Transaction ${id} is not a root move`); + return { journal, paths }; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + } + const stageJournal = path40.join(stage, "runtime", "transactions", id, "transaction.json"); + try { + const snapshot2 = await readNativeProtectedFile({ + root: stage, + file: stageJournal, + maxBytes: NATIVE_ROOT_MOVE_MAX_JOURNAL_BYTES, + label: `Staged Native root-move journal ${id}` + }); + const journal = JSON.parse(snapshot2.bytes.toString("utf8")); + if (journal.schema !== "comet.native.transaction.v1" || journal.kind !== "root-move") { + throw new Error(`Invalid staged root-move journal: ${id}`); + } + return { journal, paths: destinationPaths }; + } catch (error) { + if (error.code !== "ENOENT") throw error; + throw new Error(`Native root-move journal is missing: ${id}`, { cause: error }); + } +} +async function setPendingStage(options) { + const updated = pendingConfig( + options.config, + { ...options.pending, stage: options.stage }, + options.activeArtifactRoot + ); + await writeProjectConfig(options.projectRoot, updated); + return updated; +} +async function setPendingCleanup(options) { + const pending = options.config.native.pending_root_move; + if (!pending || pending.id !== options.id) { + throw new Error(`Native root-move cleanup ${options.id} lost its pending configuration`); + } + const updated = pendingConfig( + options.config, + { + id: pending.id, + fromArtifactRoot: pending.fromArtifactRoot, + toArtifactRoot: pending.toArtifactRoot, + stage: pending.stage, + ...options.cleanup ? { cleanup: options.cleanup } : {} + }, + options.config.native.artifact_root + ); + await writeProjectConfig(options.projectRoot, updated); + return updated; +} +async function cleanupRootMoveTree(options) { + let config = options.config; + const pending = config.native.pending_root_move; + if (!pending || pending.id !== options.id) { + throw new Error(`Native root-move cleanup ${options.id} lost its pending configuration`); + } + let cleanup = pending.cleanup; + if (cleanup && cleanup.kind !== options.kind) { + throw new Error( + `Native root move has unfinished ${cleanup.kind} cleanup; cannot start ${options.kind}` + ); + } + let targetExists = await exists3(options.target); + let quarantineExists = await exists3(options.quarantine); + if (targetExists && quarantineExists) { + throw new Error(`Native root-move cleanup target and quarantine both exist: ${options.kind}`); + } + if (!cleanup) { + if (!targetExists && !quarantineExists) return config; + if (!targetExists && quarantineExists) { + throw new Error( + `Native root-move cleanup quarantine is not transaction-bound: ${options.kind}` + ); + } + const manifestHash = await writeCleanupManifest({ + paths: options.stablePaths, + id: options.id, + kind: options.kind, + entries: await walkTree(options.target, { rejectSymlinks: true }) + }); + cleanup = { kind: options.kind, state: "prepared", manifestHash }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + const manifest = await readCleanupManifest({ + paths: options.stablePaths, + id: options.id, + cleanup + }); + targetExists = await exists3(options.target); + quarantineExists = await exists3(options.quarantine); + if (targetExists && quarantineExists) { + throw new Error(`Native root-move cleanup target and quarantine both exist: ${options.kind}`); + } + if (targetExists) { + if (cleanup.state !== "prepared") { + throw new Error( + `Native root-move cleanup target reappeared after quarantine: ${options.kind}` + ); + } + await quarantineNativeProtectedDirectory({ + root: options.projectRoot, + directory: options.target, + quarantine: options.quarantine, + label: options.label, + beforeQuarantine: options.beforeQuarantine, + afterQuarantine: options.afterQuarantine + }); + quarantineExists = true; + } + if (!quarantineExists) { + if (cleanup.state !== "deleting") { + throw new Error(`Native root-move cleanup quarantine disappeared: ${options.kind}`); + } + return setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id + }); + } + await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest, + exact: cleanup.state !== "deleting" + }); + if (cleanup.state === "prepared") { + cleanup = { ...cleanup, state: "quarantined" }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + if (cleanup.state === "quarantined") { + cleanup = { ...cleanup, state: "deleting" }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + await deleteCleanupManifestSubset({ + projectRoot: options.projectRoot, + quarantine: options.quarantine, + manifest, + hooks: options.hooks + }); + return setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id + }); +} +async function finishForwardMove(options) { + let config = options.config; + let stage = config.native.pending_root_move.stage; + if (stage === "copying") { + if (!await exists3(options.sourcePaths.nativeRoot)) { + throw new Error(`Native source root is missing: ${options.sourcePaths.nativeRoot}`); + } + const stagingRemoval = stagingRemovalDirectory(options.staging); + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: options.pending.id, + kind: "restart-staging", + stablePaths: options.sourcePaths, + target: options.staging, + quarantine: stagingRemoval, + label: "Native root move stale staging removal", + hooks: options.hooks + }); + await walkTree(options.sourcePaths.nativeRoot, { + rejectSymlinks: true, + excludedFiles: /* @__PURE__ */ new Set([path40.resolve(options.lockFile)]) + }); + await copyTree( + options.sourcePaths.nativeRoot, + options.staging, + options.lockFile, + options.destinationPaths.projectRoot + ); + await assertEquivalentTrees(options.sourcePaths.nativeRoot, options.staging, options.lockFile); + config = await setPendingStage({ + projectRoot: options.projectRoot, + config, + pending: options.pending, + stage: "ready" + }); + stage = "ready"; + await options.hooks?.afterRootMoveStage?.("ready", options.journal); + } + if (stage === "ready") { + if (await exists3(options.destinationPaths.nativeRoot)) { + if (await exists3(options.staging)) { + throw new Error(`Native destination is occupied: ${options.destinationPaths.nativeRoot}`); + } + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.destinationPaths.nativeRoot, + options.lockFile + ); + } else { + if (!await exists3(options.staging)) throw new Error(`Native move staging tree is missing`); + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.staging, + options.lockFile + ); + await fs27.rename(options.staging, options.destinationPaths.nativeRoot); + } + config = await setPendingStage({ + projectRoot: options.projectRoot, + config, + pending: options.pending, + stage: "switched", + activeArtifactRoot: options.pending.toArtifactRoot + }); + stage = "switched"; + await options.hooks?.afterRootMoveStage?.("switched", options.journal); + } + if (stage !== "switched") throw new Error(`Unsupported Native root-move stage: ${stage}`); + if (!await exists3(options.destinationPaths.nativeRoot)) { + throw new Error(`Native destination root is missing: ${options.destinationPaths.nativeRoot}`); + } + const sourceRemoval = sourceRemovalDirectory(options.sourcePaths, options.pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(options.sourcePaths.nativeRoot)) { + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.destinationPaths.nativeRoot, + options.lockFile + ); + } + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: options.pending.id, + kind: "forward-source", + stablePaths: options.destinationPaths, + target: options.sourcePaths.nativeRoot, + quarantine: sourceRemoval, + label: "Native root move source removal", + beforeQuarantine: () => options.hooks?.beforeRootMoveSourceRemove?.(options.sourcePaths.nativeRoot), + afterQuarantine: (quarantine) => options.hooks?.afterRootMoveSourceQuarantined?.(quarantine), + hooks: options.hooks + }); + await refreshNativeWorkspaceIdentities(options.destinationPaths); + const destinationJournal = await readNativeTransaction( + options.destinationPaths, + options.pending.id + ); + await finalizeNativeTransaction(options.destinationPaths, destinationJournal, "commit"); + const stableNative = { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot + }; + const committed = { + ...config, + native: { ...stableNative, artifact_root: options.pending.toArtifactRoot } + }; + await writeProjectConfig(options.projectRoot, committed); + return committed; +} +async function moveNativeRoot(options) { + const current = await readProjectConfig(options.projectRoot) ?? defaultProjectConfig("docs"); + if (current.native.pending_root_move) { + throw new Error( + `Native root move ${current.native.pending_root_move.id} is already incomplete` + ); + } + const toArtifactRoot = normalizeArtifactRootRef(options.toArtifactRoot); + if (toArtifactRoot === current.native.artifact_root) { + throw new Error(`Native artifact root is already ${toArtifactRoot}`); + } + const sourcePaths = await nativeProjectPaths(options.projectRoot, current.native.artifact_root); + const destinationPaths = await nativeProjectPaths(options.projectRoot, toArtifactRoot); + if (isInsidePath(sourcePaths.nativeRoot, destinationPaths.nativeRoot) || isInsidePath(destinationPaths.nativeRoot, sourcePaths.nativeRoot)) { + throw new Error("Native source and destination roots must not overlap"); + } + if (!await exists3(sourcePaths.nativeRoot)) { + throw new Error(`Native source root does not exist: ${sourcePaths.nativeRoot}`); + } + await assertNoUnfinishedTransactions(sourcePaths); + if (await exists3(destinationPaths.nativeRoot)) { + throw new Error(`Native destination is occupied: ${destinationPaths.nativeRoot}`); + } + const lock = await acquireNativeLock(sourcePaths, "root-move", `move root to ${toArtifactRoot}`); + const id = randomUUID9(); + const pending = { + id, + fromArtifactRoot: current.native.artifact_root, + toArtifactRoot, + stage: "copying" + }; + const journal = rootMoveJournal({ id, paths: sourcePaths, now: options.now ?? /* @__PURE__ */ new Date() }); + const staging = stagingDirectory(destinationPaths, id); + try { + await assertNoOtherLocks(sourcePaths, lock.file); + if (await exists3(staging)) throw new Error(`Native move staging path is occupied: ${staging}`); + await writeProjectConfig(options.projectRoot, pendingConfig(current, pending)); + await createNativeTransaction(sourcePaths, journal); + await options.hooks?.afterRootMoveStage?.("copying", journal); + await finishForwardMove({ + projectRoot: options.projectRoot, + config: pendingConfig(current, pending), + pending, + sourcePaths, + destinationPaths, + staging, + journal, + lockFile: lock.file, + hooks: options.hooks + }); + return { + fromNativeRoot: sourcePaths.nativeRoot, + toNativeRoot: destinationPaths.nativeRoot, + transactionId: id + }; + } finally { + await releaseNativeLock(lock); + } +} +async function recoverNativeRootMove(options) { + let config = await readProjectConfig(options.projectRoot); + const pending = config?.native.pending_root_move; + if (!config || !pending) throw new Error("No pending Native root move was found"); + const sourcePaths = await nativeProjectPaths(options.projectRoot, pending.fromArtifactRoot); + const destinationPaths = await nativeProjectPaths(options.projectRoot, pending.toArtifactRoot); + const staging = stagingDirectory(destinationPaths, pending.id); + const lockPaths = await exists3(sourcePaths.nativeRoot) ? sourcePaths : destinationPaths; + const lock = await acquireNativeLock(lockPaths, "root-move", `recover root ${pending.id}`); + try { + let journalInfo; + try { + journalInfo = await readRootMoveJournal(sourcePaths, destinationPaths, staging, pending.id); + } catch (error) { + if (pending.stage !== "copying" || !await exists3(sourcePaths.nativeRoot)) throw error; + const journal = rootMoveJournal({ id: pending.id, paths: sourcePaths, now: /* @__PURE__ */ new Date() }); + await createNativeTransaction(sourcePaths, journal); + journalInfo = { journal, paths: sourcePaths }; + } + if (options.strategy === "continue") { + const committed = await finishForwardMove({ + projectRoot: options.projectRoot, + config, + pending, + sourcePaths, + destinationPaths, + staging, + journal: journalInfo.journal, + lockFile: lock.file, + hooks: options.hooks + }); + return { activeNativeRoot: destinationPaths.nativeRoot, config: committed }; + } + if (!await exists3(sourcePaths.nativeRoot)) { + throw new Error("Cannot roll back after the old Native root was removed; continue recovery"); + } + const destinationRemoval = rollbackRemovalDirectory(destinationPaths.nativeRoot, pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(destinationPaths.nativeRoot)) { + await assertEquivalentTrees(sourcePaths.nativeRoot, destinationPaths.nativeRoot, lock.file); + } + if (!config.native.pending_root_move?.cleanup || config.native.pending_root_move.cleanup.kind === "rollback-destination") { + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: pending.id, + kind: "rollback-destination", + stablePaths: sourcePaths, + target: destinationPaths.nativeRoot, + quarantine: destinationRemoval, + label: "Native root move rollback destination removal", + hooks: options.hooks + }); + } + const stagingRemoval = rollbackRemovalDirectory(staging, pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(staging)) { + await assertEquivalentTrees(sourcePaths.nativeRoot, staging, lock.file); + } + if (!config.native.pending_root_move?.cleanup || config.native.pending_root_move.cleanup.kind === "rollback-staging") { + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: pending.id, + kind: "rollback-staging", + stablePaths: sourcePaths, + target: staging, + quarantine: stagingRemoval, + label: "Native root move rollback staging removal", + hooks: options.hooks + }); + } + const sourceJournal = await readNativeTransaction(sourcePaths, pending.id); + await rollbackNativeTransaction(sourcePaths, sourceJournal); + const stableNative = { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot + }; + const restored = { + ...config, + native: { ...stableNative, artifact_root: pending.fromArtifactRoot } + }; + await writeProjectConfig(options.projectRoot, restored); + return { activeNativeRoot: sourcePaths.nativeRoot, config: restored }; + } finally { + await releaseNativeLock(lock); + } +} + +// domains/comet-native/native-doctor.ts +var NATIVE_DOCTOR_MAX_CHANGE_ENTRIES = 4096; +var NATIVE_DOCTOR_MAX_TRANSACTION_ENTRIES = 4096; +var NATIVE_DOCTOR_MAX_LOCK_ENTRIES = 1024; +async function directoryEntries(paths, directory, maxEntries) { + try { + const protectedDirectory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory, + label: "Native doctor directory", + maxEntries + }); + await protectedDirectory.verify(); + return protectedDirectory.entries; + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } +} +async function clearStaleRecoveryLocks(targets, findings) { + const unique = new Map( + targets.map((target) => [ + path41.resolve(target.file), + { ...target, file: path41.resolve(target.file) } + ]) + ); + for (const { paths, file } of unique.values()) { + let diagnosis; + try { + diagnosis = await diagnoseNativeLock(file); + } catch (error) { + findings.push({ + severity: "error", + code: "lock-invalid", + message: `Native recovery lock is invalid: ${error.message}`, + path: file + }); + return false; + } + if (diagnosis.status === "missing") continue; + if (diagnosis.status === "stale") { + const takeover = await takeOverNativeStaleLock(paths, file, diagnosis); + if (takeover.status === "removed") { + findings.push({ + severity: "info", + code: "stale-recovery-lock-removed", + message: `Removed stale lock before explicit transaction recovery`, + path: file + }); + continue; + } + if (takeover.status === "missing") continue; + diagnosis = takeover.diagnosis; + } + if (diagnosis.status === "stale") { + findings.push({ + severity: "error", + code: "lock-takeover-raced", + message: "Native recovery lock changed while doctor was preparing stale takeover", + path: file + }); + return false; + } + findings.push({ + severity: "error", + code: diagnosis.status === "active" ? "lock-active" : "lock-owner-unknown", + message: diagnosis.status === "active" ? `Native recovery lock is still owned by a live process` : `Native recovery lock owner cannot be proven stale`, + path: file + }); + return false; + } + return true; +} +async function inspectSelection(paths, repair) { + const file = nativeSelectionFile(paths); + let value; + try { + await resolveContainedNativePath(paths.projectRoot, file); + const selection = await readNativeSelectionRecord(paths); + if (!selection) return []; + value = selection; + } catch (error) { + if (error.code === "ENOENT") return []; + return [ + { + severity: "error", + code: "selection-invalid", + message: `Native selection is invalid: ${error.message}`, + path: file + } + ]; + } + if (value.schema !== "comet.selection.v2" || value.workflow !== "native" || typeof value.change !== "string") { + return [ + { + severity: "error", + code: "selection-invalid", + message: "Native selection has an invalid schema or change name", + path: file + } + ]; + } + try { + await readNativeChange(paths, value.change); + return []; + } catch (error) { + if (error.code !== "ENOENT") { + return [ + { + severity: "error", + code: "selection-target-invalid", + message: `Selected Native change is invalid: ${error.message}`, + path: file + } + ]; + } + } + if (repair) { + await fs28.rm(file, { force: true }); + return [ + { + severity: "info", + code: "selection-cleared", + message: `Cleared stale Native selection ${value.change}`, + path: file + } + ]; + } + return [ + { + severity: "warning", + code: "selection-stale", + message: `Selected Native change does not exist: ${value.change}`, + path: file + } + ]; +} +async function inspectManagedPaths(paths) { + const findings = []; + for (const managedPath of [ + paths.specsDir, + paths.changesDir, + paths.archiveDir, + paths.runtimeDir, + paths.locksDir, + paths.transactionsDir + ]) { + try { + await resolveContainedNativePath(paths.nativeRoot, managedPath); + } catch (error) { + findings.push({ + severity: "error", + code: "native-path-unsafe", + message: `Managed Native path is unsafe: ${error.message}`, + path: managedPath + }); + } + } + return findings; +} +async function inspectTransactions(paths, options) { + const findings = []; + const unfinished = []; + for (const entry2 of await directoryEntries( + paths, + paths.transactionsDir, + NATIVE_DOCTOR_MAX_TRANSACTION_ENTRIES + )) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let journal; + try { + journal = await readNativeTransaction(paths, entry2.name); + } catch (error) { + findings.push({ + severity: "error", + code: "transaction-invalid", + message: `Native transaction ${entry2.name} is invalid: ${error.message}`, + path: path41.join(paths.transactionsDir, entry2.name) + }); + continue; + } + if (journal.status === "committed" || journal.status === "rolled-back") continue; + if (options.name && journal.change && journal.change !== options.name) continue; + if (journal.kind !== "archive") { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "root-move-transaction-orphaned", + message: `Root-move transaction ${journal.id} is incomplete but project config has no matching pending move` + }); + continue; + } + if (options.repair && options.recoveryStrategy) { + try { + const locksReady = await withNativeLockRecovery( + [paths], + `doctor archive recovery ${journal.id}`, + async () => { + const ready = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, "archive.lock") } + ], + findings + ); + if (!ready) return false; + await recoverArchiveTransaction({ + paths, + transactionId: journal.id, + strategy: options.recoveryStrategy + }); + return true; + } + ); + if (!locksReady) { + unfinished.push(journal); + continue; + } + findings.push({ + severity: "info", + code: "archive-transaction-recovered", + message: `${options.recoveryStrategy === "continue" ? "Continued" : "Rolled back"} archive transaction ${journal.id}` + }); + } catch (error) { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "archive-recovery-failed", + message: `Archive recovery failed: ${error.message}` + }); + } + } else { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "archive-transaction-incomplete", + message: options.repair ? `Archive transaction ${journal.id} needs an explicit recovery strategy` : `Archive transaction ${journal.id} is incomplete`, + repair: options.recoveryStrategy ?? "continue" + }); + } + } + return { findings, unfinished }; +} +async function inspectLocks(paths, repair, unfinished) { + const findings = []; + for (const entry2 of await directoryEntries( + paths, + paths.locksDir, + NATIVE_DOCTOR_MAX_LOCK_ENTRIES + )) { + if (!entry2.isFile() || entry2.isSymbolicLink() || !entry2.name.endsWith(".lock")) continue; + const file = path41.join(paths.locksDir, entry2.name); + try { + const diagnosis = await diagnoseNativeLock(file); + if (diagnosis.status === "active") { + findings.push({ + severity: "warning", + code: "lock-active", + message: `Native lock is active for ${diagnosis.owner?.operation ?? "an operation"}`, + path: file + }); + } else if (diagnosis.status === "unknown") { + findings.push({ + severity: "warning", + code: "lock-owner-unknown", + message: "Native lock owner cannot be proven stale", + path: file + }); + } else if (diagnosis.status === "stale") { + if (repair && unfinished.length === 0) { + const takeover = await takeOverNativeStaleLock(paths, file, diagnosis); + if (takeover.status === "removed") { + findings.push({ + severity: "info", + code: "stale-lock-removed", + message: "Removed a Native lock whose local owner process is absent", + path: file + }); + } else if (takeover.status === "changed") { + findings.push({ + severity: takeover.diagnosis.status === "active" ? "warning" : "error", + code: "lock-takeover-raced", + message: "Native lock changed while doctor was preparing stale takeover", + path: file + }); + } + } else { + findings.push({ + severity: unfinished.length > 0 ? "error" : "warning", + code: "lock-stale", + message: unfinished.length > 0 ? "Native lock is stale but an unfinished transaction still requires recovery" : "Native lock owner process is absent", + path: file + }); + } + } + } catch (error) { + findings.push({ + severity: "error", + code: "lock-invalid", + message: `Native lock metadata is invalid: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function inspectChanges(paths, name) { + const findings = []; + const statuses = name ? await listNativeStatus(paths).then((all) => all.filter((status) => status.name === name)) : await listNativeStatus(paths); + if (name && statuses.length === 0) { + return [ + { + severity: "error", + code: "change-missing", + message: `Native change does not exist: ${name}` + } + ]; + } + for (const status of statuses) { + if (status.migrationRequired) continue; + if (status.phase === "invalid") { + findings.push({ + severity: "error", + code: "change-invalid", + message: status.error ?? `Native change ${status.name} is invalid`, + path: path41.join(paths.changesDir, status.name, NATIVE_CHANGE_STATE_FILE) + }); + continue; + } + const detailed = await inspectNativeStatus(paths, status.name, { details: true }); + for (const artifact of detailed.findings ?? []) { + if (artifact.code === "trajectory-tail-incomplete" || artifact.code === "checkpoint-progress-incomplete") { + continue; + } + findings.push({ + severity: artifact.severity, + code: artifact.code, + message: `${status.name}: ${artifact.message}`, + ...artifact.path ? { path: path41.join(paths.projectRoot, artifact.path) } : {} + }); + } + } + return findings; +} +async function inspectTrajectoryTailRepairs(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + try { + const inspection = await inspectNativeTrajectoryTail(paths, name); + if (inspection.status !== "repairable") continue; + if (!options.repair) { + findings.push({ + severity: "error", + code: "trajectory-tail-incomplete", + message: `Native trajectory for ${name} has an incomplete final line ${inspection.line}; ${inspection.discardedBytes} byte(s) are outside the last complete event`, + path: inspection.file, + repair: "truncate-tail" + }); + continue; + } + const repaired = await repairNativeTrajectoryTail(paths, name); + if (repaired) { + findings.push({ + severity: "info", + code: "trajectory-tail-repaired", + message: `Removed the incomplete Native trajectory tail for ${name} and preserved all complete events`, + path: repaired.file + }); + } + } catch (error) { + findings.push({ + severity: "error", + code: "trajectory-tail-repair-failed", + message: `Native trajectory tail repair failed for ${name}: ${error.message}`, + path: path41.join(paths.changesDir, name, "runtime", "trajectory.jsonl") + }); + } + } + return findings; +} +async function inspectWorkspaceIdentityMigrations(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + try { + if (!await nativeWorkspaceIdentityNeedsMigration(paths, name)) { + continue; + } + if (!options.repair) { + findings.push({ + severity: "warning", + code: "workspace-identity-migration-required", + message: `Native workspace identity for ${name} uses legacy external-probe or hash-only metadata`, + path: nativeWorkspaceFile(paths, name), + repair: "migrate" + }); + continue; + } + const state = await readNativeChange(paths, name); + const migrated = await migrateLegacyNativeWorkspaceIdentity({ + paths, + name, + revision: state.revision + }); + if (migrated) { + findings.push({ + severity: "info", + code: "workspace-identity-migrated", + message: `Replaced legacy workspace metadata for ${name} with process-free root identities`, + path: nativeWorkspaceFile(paths, name) + }); + } + } catch (error) { + findings.push({ + severity: "error", + code: "workspace-identity-migration-failed", + message: `Native workspace identity migration failed for ${name}: ${error.message}`, + path: nativeWorkspaceFile(paths, name) + }); + } + } + return findings; +} +async function inspectSchemaMigrations(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + const file = nativeSchemaMigrationJournalFile(paths, name); + try { + const pending = await inspectPendingNativeSchemaMigration(paths, name); + const inspection = await inspectNativeChange(paths, name); + if (!pending && inspection.status === "current") continue; + if (inspection.status === "runtime-incompatible") { + findings.push({ + severity: "error", + code: "change-runtime-incompatible", + message: inspection.message ?? `Native change ${name} requires a newer runtime`, + path: path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE) + }); + continue; + } + if (!options.repair) { + findings.push({ + severity: "error", + code: pending ? "schema-migration-incomplete" : "schema-migration-required", + message: pending ? `Native schema migration ${pending.id} is incomplete for ${name}` : `Native change ${name} requires migration to the current schema`, + path: pending ? file : path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE), + repair: "migrate" + }); + continue; + } + await migrateNativeChange({ paths, name }); + findings.push({ + severity: "info", + code: pending ? "schema-migration-recovered" : "schema-migrated", + message: `Migrated Native change ${name} to the current schema`, + path: path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE) + }); + } catch (error) { + if (error.code === "ENOENT") continue; + findings.push({ + severity: "error", + code: "schema-migration-failed", + message: `Native schema migration failed for ${name}: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function inspectTransitionJournals(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + let journal; + try { + journal = await inspectPendingNativeTransition(paths, name); + } catch (error) { + if (error instanceof NativeTransitionMigrationRequiredError) continue; + findings.push({ + severity: "error", + code: "transition-invalid", + message: `Native transition journal is invalid: ${error.message}`, + path: nativeTransitionJournalFile(paths, name) + }); + continue; + } + if (!journal) continue; + if (options.repair && options.recoveryStrategy === "continue") { + try { + const recovered = await withNativeLockRecovery( + [paths], + `doctor transition recovery ${name}`, + async () => { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, `transition-${name}.lock`) } + ], + findings + ); + if (!locksReady) return false; + await continueNativeTransition(paths, name); + return true; + } + ); + if (!recovered) continue; + findings.push({ + severity: "info", + code: "transition-recovered", + message: `Continued Native phase transition ${journal.id} for ${name}`, + path: nativeTransitionJournalFile(paths, name) + }); + } catch (error) { + findings.push({ + severity: "error", + code: "transition-recovery-failed", + message: `Native transition recovery failed: ${error.message}`, + path: nativeTransitionJournalFile(paths, name) + }); + } + continue; + } + findings.push({ + severity: "error", + code: "transition-incomplete", + message: options.repair && options.recoveryStrategy === "rollback" ? `Native phase transition ${journal.id} only supports deterministic continue recovery` : `Native phase transition ${journal.id} is incomplete for ${name}`, + path: nativeTransitionJournalFile(paths, name), + repair: "continue" + }); + } + return findings; +} +async function inspectCheckpointJournals(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + const file = nativeCheckpointJournalFile(paths, name); + let journal; + try { + journal = await readNativeCheckpointJournal(paths, name); + } catch (error) { + if (error.code === "ENOENT") continue; + findings.push({ + severity: "error", + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint journal is invalid: ${error.message}`, + path: file + }); + continue; + } + if (!journal) continue; + if (!options.repair) { + findings.push({ + severity: "error", + code: "checkpoint-progress-incomplete", + message: `Native progress checkpoint ${journal.id} is incomplete for ${name}`, + path: file + }); + continue; + } + try { + const recovered = await withNativeLockRecovery( + [paths], + `doctor checkpoint recovery ${name}`, + async () => { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, `transition-${name}.lock`) } + ], + findings + ); + if (!locksReady) return false; + await continueNativeCheckpoint(paths, name); + return true; + } + ); + if (!recovered) continue; + findings.push({ + severity: "info", + code: "checkpoint-progress-recovered", + message: `Continued Native progress checkpoint ${journal.id} for ${name}`, + path: file + }); + } catch (error) { + findings.push({ + severity: "error", + code: "checkpoint-progress-recovery-failed", + message: `Native progress checkpoint recovery failed: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function doctorNativeProject(options) { + const repair = options.repair ?? false; + const findings = []; + let paths = options.paths; + let config; + try { + config = await readProjectConfig(paths.projectRoot); + } catch (error) { + const result2 = { + healthy: false, + findings: [ + { + severity: "error", + code: "config-invalid", + message: `Comet project config is invalid: ${error.message}`, + path: paths.configFile + } + ] + }; + return result2; + } + let relocationRecoveryPending = Boolean(config?.native.pending_root_move); + if (config?.native.pending_root_move) { + const pending = config.native.pending_root_move; + const [fromPaths, toPaths] = await Promise.all([ + nativeProjectPaths(paths.projectRoot, pending.fromArtifactRoot), + nativeProjectPaths(paths.projectRoot, pending.toArtifactRoot) + ]); + if (repair && options.recoveryStrategy) { + try { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths: fromPaths, file: path41.join(fromPaths.locksDir, "root-move.lock") }, + { paths: toPaths, file: path41.join(toPaths.locksDir, "root-move.lock") } + ], + findings + ); + if (!locksReady) return { healthy: false, findings }; + const recovered = await recoverNativeRootMove({ + projectRoot: paths.projectRoot, + strategy: options.recoveryStrategy + }); + paths = await nativeProjectPaths(paths.projectRoot, recovered.config.native.artifact_root); + relocationRecoveryPending = false; + findings.push({ + severity: "info", + code: "root-move-recovered", + message: `${options.recoveryStrategy === "continue" ? "Continued" : "Rolled back"} Native root move ${pending.id}` + }); + } catch (error) { + findings.push({ + severity: "error", + code: "root-move-recovery-failed", + message: `Native root recovery failed: ${error.message}` + }); + return { healthy: false, findings }; + } + } else { + findings.push({ + severity: "error", + code: "root-move-incomplete", + message: `Native root move ${pending.id} is ${pending.stage}; inspect ${fromPaths.nativeRoot} and ${toPaths.nativeRoot}`, + repair: options.recoveryStrategy ?? "continue" + }); + } + } + const managedPathFindings = await inspectManagedPaths(paths); + findings.push(...managedPathFindings); + if (managedPathFindings.length > 0) return { healthy: false, findings }; + const transactions = await inspectTransactions(paths, { + name: options.name, + repair, + recoveryStrategy: options.recoveryStrategy + }); + findings.push(...transactions.findings); + findings.push(...await inspectSchemaMigrations(paths, { name: options.name, repair })); + findings.push( + ...await inspectWorkspaceIdentityMigrations(paths, { name: options.name, repair }) + ); + findings.push(...await inspectTrajectoryTailRepairs(paths, { name: options.name, repair })); + findings.push( + ...await inspectTransitionJournals(paths, { + name: options.name, + repair, + recoveryStrategy: options.recoveryStrategy + }) + ); + findings.push(...await inspectCheckpointJournals(paths, { name: options.name, repair })); + findings.push( + ...await inspectNativeEvidenceRetention({ + paths, + name: options.name, + repair, + now: options.now, + deferAll: relocationRecoveryPending || transactions.unfinished.length > 0 + }) + ); + findings.push(...await inspectLocks(paths, repair, transactions.unfinished)); + findings.push(...await inspectSelection(paths, repair)); + findings.push(...await inspectChanges(paths, options.name)); + return { + healthy: findings.every((finding) => finding.severity === "info"), + findings + }; +} + +// domains/comet-native/native-check-receipt.ts +import { createHash as createHash15 } from "node:crypto"; +import { constants as fsConstants6, promises as fs29 } from "node:fs"; +import path42 from "node:path"; +import { TextDecoder as TextDecoder5 } from "node:util"; +var ISSUE_KIND_RANK = { + "conflict-marker": 0, + "trailing-whitespace": 1, + "space-before-tab": 2, + "scope-mismatch": 3, + "unsafe-file": 4, + "scan-limit": 5 +}; +var ScopedFileError = class extends Error { + constructor(kind, message) { + super(message); + this.kind = kind; + } + kind; +}; +function projectionManifest2(projection) { + return { + schema: "comet.native.content-snapshot.v1", + origin: projection.origin, + ...projection.capture ? { capture: projection.capture } : {}, + createdAt: "1970-01-01T00:00:00.000Z", + complete: projection.complete, + limits: projection.limits, + ...projection.policy ? { policy: projection.policy } : {}, + entries: projection.entries, + omitted: projection.omitted, + omittedCount: projection.omittedCount, + ...projection.omissionOverflow ? { omissionOverflow: projection.omissionOverflow } : {} + }; +} +async function collectBoundFacts(options) { + const baseline = projectionManifest2(options.scope.baseline); + const [contract, snapshot2] = await Promise.all([ + collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }), + createNativeCurrentContentSnapshot(options.paths, baseline, { origin: "explicit" }) + ]); + const currentBundle = buildNativeImplementationScopeBundle({ + baseline, + current: snapshot2, + contractHash: options.scope.authority.contractHash, + declaredArtifacts: options.scope.authority.declaredArtifacts, + noCodeReason: options.scope.authority.noCodeReason, + ...options.scope.authority.gitChangedPaths ? { gitChangedPaths: options.scope.authority.gitChangedPaths } : {} + }); + return { + contractHash: contract.contract.contractHash, + snapshotHash: currentBundle.scope.currentProjectionHash + }; +} +function receiptTime(clock, label) { + const value = (clock ?? (() => /* @__PURE__ */ new Date()))(); + if (!(value instanceof Date) || !Number.isFinite(value.getTime())) { + throw new Error(`Native check ${label} time is invalid`); + } + return value.toISOString(); +} +function staleReasons(before, after, scope) { + const reasons = []; + const expectedContractHash = scope.scope.contractHash; + const expectedSnapshotHash = scope.scope.currentProjectionHash; + if (before.contractHash !== expectedContractHash) { + reasons.push("contract-before-does-not-match-scope"); + } + if (before.snapshotHash !== expectedSnapshotHash) { + reasons.push("implementation-before-does-not-match-scope"); + } + if (after.contractHash !== before.contractHash) { + reasons.push("contract-changed-during-check"); + } + if (after.snapshotHash !== before.snapshotHash) { + reasons.push("implementation-changed-during-check"); + } + if (after.contractHash === before.contractHash && after.contractHash !== expectedContractHash) { + reasons.push("contract-after-does-not-match-scope"); + } + if (after.snapshotHash === before.snapshotHash && after.snapshotHash !== expectedSnapshotHash) { + reasons.push("implementation-after-does-not-match-scope"); + } + return reasons; +} +function sameDirectoryIdentity7(identity, stat) { + const stableMetadata = identity.birthtimeMs === stat.birthtimeMs && identity.ctimeMs === stat.ctimeMs; + return stableMetadata && sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity8(left, right) { + const stableMetadata = left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; + return stableMetadata && sameFileObject( + { ...left, birthtime: left.birthtimeMs }, + { + ...right, + birthtime: right.birthtimeMs + } + ); +} +async function captureProjectDirectoryChain(projectRoot, directory) { + const lexicalRoot = path42.resolve(projectRoot); + const lexicalDirectory = path42.resolve(directory); + if (!isInsidePath(lexicalRoot, lexicalDirectory)) { + throw new ScopedFileError("unsafe-file", "Scoped file parent escapes the project root"); + } + const chain = []; + let cursor = lexicalRoot; + for (const segment of [ + "", + ...path42.relative(lexicalRoot, lexicalDirectory).split(path42.sep).filter(Boolean) + ]) { + if (segment) cursor = path42.join(cursor, segment); + let stat; + try { + stat = await fs29.lstat(cursor); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file parent no longer exists"); + } + throw error; + } + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new ScopedFileError("unsafe-file", "Scoped file parent is not a real directory"); + } + const realPath = await fs29.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file parent resolves outside the project"); + } + chain.push({ + path: cursor, + realPath, + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs + }); + } + return { physicalRoot: chain[0].realPath, chain }; +} +async function verifyProjectDirectoryChain(chain) { + for (const identity of chain) { + let stat; + try { + stat = await fs29.lstat(identity.path); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("unsafe-file", "Scoped file parent changed while reading"); + } + throw error; + } + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity7(identity, stat) || await fs29.realpath(identity.path) !== identity.realPath) { + throw new ScopedFileError("unsafe-file", "Scoped file parent changed while reading"); + } + } +} +async function readScopedFile(options) { + const lexicalFile = path42.resolve(options.projectRoot, ...options.file.path.split("/")); + if (!isInsidePath(options.projectRoot, lexicalFile)) { + throw new ScopedFileError("unsafe-file", "Scoped file path escapes the project root"); + } + const { physicalRoot, chain } = await captureProjectDirectoryChain( + options.projectRoot, + path42.dirname(lexicalFile) + ); + let before; + try { + before = await fs29.lstat(lexicalFile); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file no longer exists"); + } + throw error; + } + if (!before.isFile() || before.isSymbolicLink()) { + throw new ScopedFileError("unsafe-file", "Scoped file is not a real regular file"); + } + if (before.size !== options.file.expected.size) { + throw new ScopedFileError( + "scope-mismatch", + "Scoped file size no longer matches its projection" + ); + } + const beforeRealPath = await fs29.realpath(lexicalFile); + if (!isInsidePath(physicalRoot, beforeRealPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file resolves outside the project root"); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants6.O_RDONLY | fsConstants6.O_NOFOLLOW | fsConstants6.O_NONBLOCK; + const handle = await fs29.open(lexicalFile, openFlags).catch((error) => { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file no longer exists"); + } + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new ScopedFileError("unsafe-file", "Scoped file became unsafe while opening"); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs29.lstat(lexicalFile), + fs29.realpath(lexicalFile) + ]); + await verifyProjectDirectoryChain(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity8(before, opened) || !sameFileIdentity8(opened, pathAfterOpen)) { + throw new ScopedFileError("unsafe-file", "Scoped file changed while opening"); + } + const chunks = []; + const digest = createHash15("sha256"); + const buffer = Buffer.allocUnsafe(64 * 1024); + let total = 0; + while (true) { + const remaining = options.file.expected.size + 1 - total; + if (remaining < 1) { + throw new ScopedFileError("scope-mismatch", "Scoped file exceeds its projected size"); + } + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > options.file.expected.size) { + throw new ScopedFileError("scope-mismatch", "Scoped file exceeds its projected size"); + } + const chunk = Buffer.from(buffer.subarray(0, read.bytesRead)); + chunks.push(chunk); + digest.update(chunk); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs29.lstat(lexicalFile), + fs29.realpath(lexicalFile) + ]); + await verifyProjectDirectoryChain(chain); + if (total !== options.file.expected.size || digest.digest("hex") !== options.file.expected.hash) { + throw new ScopedFileError( + "scope-mismatch", + "Scoped file content no longer matches its projection" + ); + } + if (!afterHandle.isFile() || !afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !isInsidePath(physicalRoot, afterRealPath) || !sameFileIdentity8(opened, afterHandle) || !sameFileIdentity8(opened, afterPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file changed while reading"); + } + const content = Buffer.concat(chunks, total); + if (content.includes(0)) return { bytes: total, text: null }; + try { + return { bytes: total, text: new TextDecoder5("utf-8", { fatal: true }).decode(content) }; + } catch { + return { bytes: total, text: null }; + } + } finally { + await handle.close(); + } +} +function selectedScopedFiles(scope) { + const current = new Map(scope.current.entries.map((entry2) => [entry2.path, entry2])); + const files = []; + const mismatches = []; + for (const change of scope.scope.changes) { + if (!change.after) continue; + const entry2 = current.get(change.path); + if (!entry2 || entry2.hash !== change.after.hash || entry2.size !== change.after.size || entry2.type !== "file") { + mismatches.push(change.path); + continue; + } + files.push({ path: change.path, expected: change.after }); + } + files.sort((left, right) => left.path.localeCompare(right.path, "en")); + mismatches.sort((left, right) => left.localeCompare(right, "en")); + return { files, mismatches }; +} +function inspectText(pathRef, text, addIssue) { + const lines = text.split("\n"); + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index].endsWith("\r") ? lines[index].slice(0, -1) : lines[index]; + const lineNumber = index + 1; + if (/^(?:<{7}|={7}|>{7})(?: |$)/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "conflict-marker" }); + } + if (/[ \t]+$/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "trailing-whitespace" }); + } + if (/^ +\t/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "space-before-tab" }); + } + } +} +async function executeNativeCheckReceipt(options) { + if (!options.state.implementation_scope) { + throw new Error("Native scoped check requires an implementation scope"); + } + const scope = await readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ); + const before = await collectBoundFacts({ paths: options.paths, state: options.state, scope }); + const startedAt = receiptTime(options.clock, "start"); + const selected = selectedScopedFiles(scope); + const issues = []; + let issueCount = 0; + const addIssue = (issue) => { + issueCount += 1; + if (issues.length < NATIVE_CHECK_LIMITS.maxIssues) issues.push(issue); + }; + let candidates = selected.files; + const selectedCount = selected.files.length + selected.mismatches.length; + const scopeDetailOverflow2 = scope.scope.unresolvedScopes.some( + (unresolved) => unresolved.kind === "scope-detail-overflow" + ); + if (scopeDetailOverflow2) { + const overflowPath = scope.scope.changes.at(-1)?.path ?? scope.current.entries.at(-1)?.path ?? scope.baseline.entries.at(-1)?.path; + if (!overflowPath) { + throw new Error("Native implementation scope overflow has no bounded diagnostic path"); + } + addIssue({ path: overflowPath, line: 1, kind: "scan-limit" }); + candidates = []; + } else if (selectedCount > NATIVE_CHECK_LIMITS.maxFiles) { + const selectedPaths = [...selected.files.map((file) => file.path), ...selected.mismatches].sort( + (left, right) => left.localeCompare(right, "en") + ); + addIssue({ + path: selectedPaths[NATIVE_CHECK_LIMITS.maxFiles], + line: 1, + kind: "scan-limit" + }); + candidates = []; + } else { + let total = 0; + for (const file of selected.files) { + if (file.expected.size > NATIVE_CHECK_LIMITS.maxFileBytes) { + addIssue({ path: file.path, line: 1, kind: "scan-limit" }); + candidates = []; + break; + } + total += file.expected.size; + if (total > NATIVE_CHECK_LIMITS.maxTotalBytes) { + addIssue({ path: file.path, line: 1, kind: "scan-limit" }); + candidates = []; + break; + } + } + } + for (const mismatch of selected.mismatches) { + addIssue({ path: mismatch, line: 1, kind: "scope-mismatch" }); + } + let filesScanned = 0; + let binaryFilesSkipped = 0; + let bytesScanned = 0; + for (const file of candidates) { + try { + const scanned = await readScopedFile({ projectRoot: options.paths.projectRoot, file }); + bytesScanned += scanned.bytes; + if (scanned.text === null) { + binaryFilesSkipped += 1; + } else { + filesScanned += 1; + inspectText(file.path, scanned.text, addIssue); + } + } catch (error) { + if (!(error instanceof ScopedFileError)) throw error; + addIssue({ path: file.path, line: 1, kind: error.kind }); + } + } + const after = await collectBoundFacts({ paths: options.paths, state: options.state, scope }); + const endedAt = receiptTime(options.clock, "end"); + const reasons = staleReasons(before, after, scope); + issues.sort( + (left, right) => left.path.localeCompare(right.path, "en") || left.line - right.line || ISSUE_KIND_RANK[left.kind] - ISSUE_KIND_RANK[right.kind] + ); + const receipt = buildNativeCheckReceipt({ + change: options.state.name, + sourceRevision: options.state.revision, + status: issueCount === 0 && reasons.length === 0 ? "passed" : "failed", + startedAt, + endedAt, + contract: { + expectedHash: scope.scope.contractHash, + beforeHash: before.contractHash, + afterHash: after.contractHash + }, + implementation: { + scopeHash: scope.scope.scopeHash, + expectedSnapshotHash: scope.scope.currentProjectionHash, + beforeSnapshotHash: before.snapshotHash, + afterSnapshotHash: after.snapshotHash + }, + counts: { + filesSelected: selectedCount, + filesScanned, + binaryFilesSkipped, + bytesScanned, + issueCount, + recordedIssueCount: issues.length + }, + issues, + issuesTruncated: issueCount > issues.length, + stale: reasons.length > 0, + staleReasons: reasons + }); + const ref = await writeNativeCheckReceipt({ + paths: options.paths, + name: options.state.name, + receipt + }); + return { receipt: parseNativeCheckReceipt(receipt), ref }; +} + +// domains/comet-native/native-check.ts +async function checkNativeChange(options) { + return withNativeMutationLock( + options.paths, + `check ${options.name}`, + () => withNativeTransitionLock(options.paths, options.name, `check ${options.name}`, async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + if (state.phase !== "verify") { + throw new Error(`Native check requires Verify, got ${state.phase}`); + } + if (!state.implementation_scope) { + throw new Error("Native check requires an implementation scope"); + } + const executed = await executeNativeCheckReceipt({ + paths: options.paths, + state + }); + return { change: state, receipt: executed.receipt, ref: executed.ref }; + }) + ); +} + +// domains/comet-native/native-progress-checkpoint.ts +import { randomUUID as randomUUID10 } from "crypto"; +function requiredText2(value, label) { + const normalized = redactNativeCredentialText(value).trim(); + if (normalized.length === 0 || normalized.length > 2e3) { + throw new Error(`${label} must be between 1 and 2000 characters`); + } + return normalized; +} +function expectedRevisionValue(value) { + if (value === void 0) return void 0; + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native expected revision must be a positive integer"); + } + return value; +} +async function checkpointNativeChange(options) { + const summary = requiredText2(options.summary, "Checkpoint summary"); + const nextAction = requiredText2(options.nextAction, "Checkpoint next action"); + const expectedRevision = expectedRevisionValue(options.expectedRevision); + return withNativeMutationLock( + options.paths, + `checkpoint ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `checkpoint ${options.name}`, + async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + const manifest = await createNativeCheckpointManifest( + options.paths, + options.name, + options.artifacts ?? [] + ); + const manifestHash = hashNativeCheckpointManifest(manifest); + const inputHash = sha256Text( + JSON.stringify({ + summary, + nextAction, + artifacts: manifest.artifacts + }) + ); + const existing = await readNativeProgressCheckpoint(options.paths, options.name); + if (existing?.inputHash === inputHash && existing.stateRevision === state.revision && existing.phase === state.phase) { + if (expectedRevision !== void 0 && expectedRevision !== existing.previousRevision && expectedRevision !== state.revision) { + throw new NativeChangeRevisionConflictError( + state.name, + expectedRevision, + state.revision + ); + } + return { + change: state, + checkpoint: existing, + idempotent: true, + expectedRevision: expectedRevision ?? existing.previousRevision, + previousRevision: existing.previousRevision, + revision: state.revision, + outcome: "idempotent", + continuation: nativeContinuation({ state }) + }; + } + if (expectedRevision !== void 0 && state.revision !== expectedRevision) { + throw new NativeChangeRevisionConflictError(state.name, expectedRevision, state.revision); + } + const nextState = { ...state, revision: state.revision + 1 }; + const checkpoint = { + schema: "comet.native.progress-checkpoint.v1", + id: options.checkpointId?.() ?? randomUUID10(), + change: state.name, + phase: state.phase, + previousRevision: state.revision, + stateRevision: nextState.revision, + summary, + nextAction, + inputHash, + manifestHash, + manifestRef: nativeCheckpointManifestRef(manifestHash), + artifactCount: manifest.artifacts.length, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString() + }; + const journal = await prepareNativeCheckpointJournal({ + paths: options.paths, + previousState: state, + nextState, + checkpoint, + manifest, + now: options.now, + checkpointId: () => checkpoint.id + }); + await options.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeCheckpointLocked( + options.paths, + options.name, + options.hooks + ); + if (!persisted) throw new Error("Native checkpoint journal disappeared before completion"); + return { + change: persisted.nextState, + checkpoint: persisted.checkpoint, + idempotent: false, + expectedRevision: expectedRevision ?? persisted.checkpoint.previousRevision, + previousRevision: persisted.checkpoint.previousRevision, + revision: persisted.nextState.revision, + outcome: "recorded", + continuation: nativeContinuation({ state: persisted.nextState }) + }; + } + ) + ); +} + +// domains/comet-native/native-specs.ts +import { promises as fs30 } from "fs"; +import path43 from "path"; +var MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES = NATIVE_CONTRACT_FILE_LIMITS.maxSpecs * 4; +async function optionalHash3(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function proposedCapabilities(paths, name) { + const specsDir = path43.join(nativeChangeDir(paths, name), "specs"); + let guard; + try { + guard = await captureNativeProtectedDirectoryGuard({ + root: paths.nativeRoot, + directory: specsDir, + label: "Native proposed specs directory" + }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const capabilities = []; + let entryCount = 0; + const directory = await fs30.opendir(specsDir); + try { + for await (const entry2 of directory) { + entryCount += 1; + if (entryCount > MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES) { + throw new Error( + `Native proposed specs directory exceeds ${MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES} entries` + ); + } + if (entry2.isSymbolicLink()) { + throw new Error(`Proposed spec capability must not be a symbolic link: ${entry2.name}`); + } + if (!entry2.isDirectory()) continue; + assertNativeName(entry2.name); + const source = path43.join(specsDir, entry2.name, "spec.md"); + await resolveContainedNativePath(paths.nativeRoot, source); + let stat; + try { + stat = await fs30.lstat(source); + } catch (error) { + if (error.code === "ENOENT") continue; + throw error; + } + if (stat.isSymbolicLink() || !stat.isFile()) { + throw new Error(`Proposed spec must be a regular file: ${entry2.name}`); + } + capabilities.push(entry2.name); + if (capabilities.length > NATIVE_CONTRACT_FILE_LIMITS.maxSpecs) { + throw new Error("Native proposed specs exceed the spec-count budget"); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + await guard.verify(); + return capabilities.sort(); +} +async function reconcileNativeSpecChanges(paths, state) { + const previous = new Map(state.spec_changes.map((change) => [change.capability, change])); + const proposed = await proposedCapabilities(paths, state.name); + const changes = []; + for (const capability of proposed) { + const existing = previous.get(capability); + if (existing?.operation === "remove") { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + if (existing) { + changes.push({ + ...existing, + source: `specs/${capability}/spec.md` + }); + continue; + } + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + changes.push({ + capability, + operation: baseHash === null ? "create" : "replace", + source: `specs/${capability}/spec.md`, + base_hash: baseHash + }); + } + for (const change of state.spec_changes) { + if (change.operation === "remove" && !proposed.includes(change.capability)) { + changes.push(change); + } + } + return changes.sort((left, right) => left.capability.localeCompare(right.capability)); +} +async function refreshNativeSpecChanges(paths, state) { + const proposed = await proposedCapabilities(paths, state.name); + const changes = []; + for (const capability of proposed) { + const existing = state.spec_changes.find((change) => change.capability === capability); + if (existing?.operation === "remove") { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + changes.push({ + capability, + operation: baseHash === null ? "create" : "replace", + source: `specs/${capability}/spec.md`, + base_hash: baseHash + }); + } + for (const change of state.spec_changes) { + if (change.operation !== "remove" || proposed.includes(change.capability)) continue; + const canonical = canonicalSpecPath(paths, change.capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + if (baseHash !== null) { + changes.push({ ...change, base_hash: baseHash }); + } + } + return changes.sort((left, right) => left.capability.localeCompare(right.capability)); +} +async function rebaseNativeSpecChanges(options) { + assertNativeName(options.name); + const summary = redactNativeCredentialText(options.summary); + assertNativeTrajectoryText(summary, "Spec rebase summary"); + return withNativeMutationLock( + options.paths, + `rebase specs for ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `rebase specs for ${options.name}`, + async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + if (state.phase === "shape") { + throw new Error("Shape spec metadata is refreshed by the next command"); + } + if (state.archived) throw new Error(`Native change ${state.name} is already archived`); + const changeDir = nativeChangeDir(options.paths, options.name); + const run = await readNativeRunState(changeDir); + if (!run || run.runId !== state.run_id || run.currentStep !== state.phase || run.pending) { + throw new Error(`Native Run state is missing or inconsistent for ${state.name}`); + } + const specChanges = await refreshNativeSpecChanges(options.paths, state); + const nextState = { + ...state, + revision: state.revision + 1, + phase: "build", + spec_changes: specChanges, + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + }; + const nextRun = { + ...run, + currentStep: "build", + iteration: run.iteration + 1, + pending: null, + status: "running" + }; + const evidenceHash = sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: state.name, + summary, + specChanges + }) + ); + await prepareNativeTransition({ + paths: options.paths, + previousState: state, + nextState, + previousRun: run, + nextRun, + evidenceHash, + eventData: { + previousPhase: state.phase, + nextPhase: "build", + evidenceHash, + summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }, + operation: "spec-rebase", + now: options.now, + transitionId: options.transitionId + }); + const rebased = await continueNativeTransitionLocked(options.paths, options.name); + if (!rebased) throw new Error("Native spec rebase journal disappeared before completion"); + return rebased; + } + ) + ); +} +async function markNativeSpecRemoval(paths, name, capability) { + assertNativeName(name); + assertNativeName(capability); + return withNativeMutationLock( + paths, + `remove spec ${capability} from ${name}`, + () => withNativeTransitionLock(paths, name, `remove spec ${capability} from ${name}`, async () => { + await settleNativeChangeJournalsLocked(paths, name); + return markNativeSpecRemovalLocked(paths, name, capability); + }) + ); +} +async function markNativeSpecRemovalLocked(paths, name, capability) { + const state = await readNativeChange(paths, name); + if (state.phase === "archive" || state.archived) { + throw new Error(`Native change ${name} no longer accepts spec changes`); + } + const proposed = await proposedCapabilities(paths, name); + if (proposed.includes(capability)) { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + const previous = state.spec_changes.find((change) => change.capability === capability); + if (previous?.operation === "remove") return state; + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + if (baseHash === null) throw new Error(`Canonical spec is missing: ${capability}`); + const updated = { + ...state, + spec_changes: [ + ...state.spec_changes.filter((change) => change.capability !== capability), + { capability, operation: "remove", base_hash: baseHash } + ].sort((left, right) => left.capability.localeCompare(right.capability)) + }; + await compareAndSwapNativeChangeLocked(paths, updated, state.revision); + return updated; +} +async function readNativeProposedSpecs(paths, name) { + const changeDir = nativeChangeDir(paths, name); + const result2 = {}; + let totalBytes = 0; + for (const capability of await proposedCapabilities(paths, name)) { + const source = await readNativeBoundedTextFile({ + root: changeDir, + ref: `specs/${capability}/spec.md`, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + totalBytes += source.size; + if (totalBytes > NATIVE_CONTRACT_FILE_LIMITS.maxTotalBytes) { + throw new Error("Native proposed specs exceed the total byte budget"); + } + result2[capability] = source.text; + } + return result2; +} + +// domains/comet-native/native-transitions.ts +import { randomUUID as randomUUID11 } from "crypto"; + +// domains/comet-native/native-guards.ts +import { promises as fs31 } from "fs"; +import path44 from "path"; +function validation(findings) { + return { valid: findings.length === 0, findings }; +} +async function validateBuildArtifacts(paths, evidence) { + const findings = []; + if ((evidence.noCodeReason ?? "").trim().length > 0) return findings; + if (!evidence.artifacts || evidence.artifacts.length === 0) { + return [ + { + code: "build-evidence-missing", + message: "Build requires an artifact reference or an explicit no-code reason" + } + ]; + } + for (const artifact of evidence.artifacts) { + if (path44.isAbsolute(artifact) || artifact.split(/[\\/]/u).includes("..") || /^(?:[A-Za-z]:|~|[\\/])/u.test(artifact)) { + findings.push({ + code: "build-artifact-unsafe", + message: `Unsafe build artifact: ${artifact}` + }); + continue; + } + const target = path44.resolve(paths.projectRoot, ...artifact.split(/[\\/]/u)); + if (!isInsidePath(paths.projectRoot, target)) { + findings.push({ + code: "build-artifact-unsafe", + message: `Unsafe build artifact: ${artifact}` + }); + continue; + } + try { + await fs31.access(target); + } catch { + findings.push({ + code: "build-artifact-missing", + message: `Build artifact does not exist: ${artifact}`, + path: artifact + }); + } + } + return findings; +} +async function inspectNativeGuard(options) { + const findings = []; + const changeDir = nativeChangeDir(options.paths, options.state.name); + if (options.evidence.summary.trim().length === 0) { + findings.push({ + code: "transition-summary-missing", + message: "Phase transition requires a summary" + }); + } + if (options.evidence.confirmed && options.state.phase !== "shape" && options.state.phase !== "build") { + findings.push({ + code: "confirmation-not-shape", + message: "Explicit confirmation is only valid while leaving Shape or Build" + }); + } + findings.push(...await inspectNativeRunConsistency(options.paths, options.state)); + if (options.state.phase === "shape" || options.state.phase === "build") { + const capturedBaseline = await readNativeBaselineManifest(options.paths, options.state.name); + if (capturedBaseline === null) { + findings.push({ + code: "baseline-snapshot-missing", + message: "Native baseline is missing; restore a trusted baseline before advancing" + }); + } else { + const baseline = await filterNativeContentSnapshotToProjectScope( + options.paths, + capturedBaseline + ); + if (!baseline.complete) { + findings.push({ + code: "baseline-snapshot-incomplete", + message: `Native baseline is incomplete within the project-owned scope (${baseline.omittedCount} omitted entries); resolve the omissions before advancing` + }); + } + } + } + if (options.state.phase === "shape") { + const brief = await validateNativeBrief(changeDir, options.state.brief); + const specs = await validateNativeSpecChanges(options.paths, options.state); + findings.push(...brief.findings, ...specs.findings); + if (findings.length === 0 && !options.evidence.confirmed) { + findings.push({ + code: "shape-confirmation-required", + message: "Native clarification requires explicit user confirmation of the shared understanding before Build" + }); + } + } else if (options.state.phase === "build") { + findings.push( + ...(await validateNativeBrief(changeDir, options.state.brief)).findings, + ...(await validateNativeSpecChanges(options.paths, options.state)).findings + ); + findings.push(...await validateBuildArtifacts(options.paths, options.evidence)); + if (findings.length === 0 && options.state.approval !== "confirmed" && !options.evidence.confirmed) { + findings.push({ + code: "approval-confirmation-required", + message: "Native approval is implicit; confirm the current shared understanding before leaving Build" + }); + } + } else if (options.state.phase === "verify") { + const report = options.evidence.verificationReport ?? options.state.verification_report; + if (!report) { + findings.push({ + code: "verification-report-missing", + message: "Verify requires a report path" + }); + } else { + findings.push(...(await validateNativeVerification(changeDir, report)).findings); + } + if (!options.evidence.verificationResult) { + findings.push({ + code: "verification-result-missing", + message: "Verify requires pass or fail" + }); + } + findings.push(...(await validateNativeSpecChanges(options.paths, options.state)).findings); + } else { + findings.push({ + code: "archive-command-required", + message: "Use comet native archive for the Archive phase" + }); + } + return validation(findings); +} + +// domains/comet-native/native-build-evidence.ts +import { promises as fs32 } from "node:fs"; +import path45 from "node:path"; +var NATIVE_BUILD_EVIDENCE_LIMITS = { + maxDeclaredArtifacts: 128, + maxArtifactPathBytes: 512 +}; +function assertStableNativeSelection(snapshot2, source) { + if (snapshot2.omitted.some((omission) => omission.reason === "git-selection-changed")) { + throw new Error( + `Native Git selection changed while capturing the ${source}; stabilize the Git index and retry Build evidence` + ); + } + if (snapshot2.omitted.some( + (omission) => omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed" + )) { + throw new Error( + `Native physical selection was incomplete or changed while capturing the ${source}; retry Build evidence with a stable bounded project tree` + ); + } +} +function nativeBaselineIncompleteError(change, baseline) { + const samplePaths = baseline.omitted.slice(0, 20).map((omission) => omission.path); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + return new NativeBaselineIncompleteError( + change, + baseline.omittedCount, + omittedByReason, + samplePaths, + baseline.omitted.length > samplePaths.length || overflowCount > 0 + ); +} +function nativeBaselineProjectionIncompleteError(change, baseline, projection) { + const retained = new Set(projection.entries.map((entry2) => entry2.path)); + const removedPaths = baseline.entries.filter((entry2) => !retained.has(entry2.path)).slice(0, 20).map((entry2) => entry2.path); + return new NativeBaselineIncompleteError( + change, + projection.omittedCount, + { "manifest-size": projection.omittedCount }, + removedPaths, + projection.omittedCount > removedPaths.length + ); +} +function normalizeProjectRef(value, label) { + const normalized = value.replaceAll("\\", "/").trim(); + if (normalized.length === 0 || normalized !== value.replaceAll("\\", "/") || path45.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(normalized) || normalized.split("/").includes("..") || path45.posix.normalize(normalized) !== normalized || normalized === "." || normalized.endsWith("/") || Buffer.byteLength(normalized, "utf8") > NATIVE_BUILD_EVIDENCE_LIMITS.maxArtifactPathBytes || Array.from(normalized).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + })) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return normalized; +} +function baselineArtifactKind(baseline, artifactRef) { + if (baseline.entries.some((entry2) => entry2.path === artifactRef)) return "file"; + if (baseline.entries.some((entry2) => entry2.path.startsWith(`${artifactRef}/`))) { + return "directory"; + } + return null; +} +async function inspectDeclaredArtifact(paths, baseline, rawRef) { + const artifactRef = normalizeProjectRef(rawRef, "Native build artifact"); + const sensitiveReason = nativeSensitiveArtifactReason(paths, artifactRef); + if (sensitiveReason) { + throw new Error(`Native build artifact is excluded as ${sensitiveReason}: ${artifactRef}`); + } + const target = path45.resolve(paths.projectRoot, ...artifactRef.split("/")); + if (!isInsidePath(paths.projectRoot, target)) { + throw new Error(`Native build artifact escapes the project: ${artifactRef}`); + } + await resolveContainedNativePath(paths.projectRoot, target); + try { + const stat = await fs32.lstat(target); + if (stat.isSymbolicLink()) { + throw new Error(`Native build artifact must not be a symlink or junction: ${artifactRef}`); + } + const realTarget = await fs32.realpath(target); + const realProjectRoot = await fs32.realpath(paths.projectRoot); + if (!isInsidePath(realProjectRoot, realTarget)) { + throw new Error(`Native build artifact resolves outside the project: ${artifactRef}`); + } + if (stat.isFile()) return { path: artifactRef, kind: "file" }; + if (stat.isDirectory()) return { path: artifactRef, kind: "directory" }; + throw new Error(`Native build artifact is not a file or directory: ${artifactRef}`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + const kind = baselineArtifactKind(baseline, artifactRef); + if (kind === null) { + throw new Error(`Native build artifact does not exist: ${artifactRef}`, { cause: error }); + } + return { path: artifactRef, kind }; + } +} +async function collectDeclaredArtifacts(options) { + if (options.refs.length > NATIVE_BUILD_EVIDENCE_LIMITS.maxDeclaredArtifacts) { + throw new Error("Native build evidence exceeds its declared-artifact budget"); + } + const artifacts = await Promise.all( + options.refs.map( + (reference) => inspectDeclaredArtifact(options.paths, options.baseline, reference) + ) + ); + artifacts.sort( + (left, right) => left.path.localeCompare(right.path) || left.kind.localeCompare(right.kind) + ); + if (new Set(artifacts.map((artifact) => artifact.path)).size !== artifacts.length) { + throw new Error("Native build evidence contains duplicate or conflicting artifacts"); + } + return artifacts; +} +function partialFindings(unresolvedScopes) { + return unresolvedScopes.map((scope) => ({ + code: "verification-scope-partial", + message: `${scope.id}: ${scope.reason}`, + ...scope.path === null ? {} : { path: scope.path } + })); +} +function partialScopeHash(value) { + if (!/^[a-f0-9]{64}$/u.test(value)) { + throw new Error("Native partial allowance requires a scope hash"); + } + return value; +} +async function inspectNativeBuildEvidence(options) { + if (options.state.phase !== "build") { + throw new Error(`Native build evidence requires Build, got ${options.state.phase}`); + } + if ((options.noCodeReason ?? "").trim().length > 0 && options.artifactRefs.length > 0) { + throw new Error("Native build evidence cannot combine artifacts with a no-code reason"); + } + const storedBaseline = await readNativeBaselineManifest(options.paths, options.state.name); + if (storedBaseline === null) throw new Error("Native change has no baseline content snapshot"); + const baseline = await filterNativeContentSnapshotToProjectScope(options.paths, storedBaseline); + assertStableNativeSelection(baseline, "baseline projection"); + if (!baseline.complete) { + throw nativeBaselineIncompleteError(options.state.name, baseline); + } + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }); + const declaredArtifacts = await collectDeclaredArtifacts({ + paths: options.paths, + baseline, + refs: options.artifactRefs + }); + const current = await createNativeCurrentContentSnapshot(options.paths, baseline, { + origin: "explicit", + now: options.now + }); + assertStableNativeSelection(current, "current snapshot"); + const bundle = buildNativeImplementationScopeBundle({ + baseline, + current, + contractHash: contract.contract.contractHash, + declaredArtifacts, + noCodeReason: options.noCodeReason ?? null + }); + if (!bundle.baseline.complete) { + throw nativeBaselineProjectionIncompleteError(options.state.name, baseline, bundle.baseline); + } + const scopeRef = nativeEvidenceRef("scopes", bundle.scope.scopeHash); + if (bundle.scope.complete) { + if (options.allowPartialScopeHash !== void 0 && options.allowPartialScopeHash !== null || options.partialReason !== void 0 && options.partialReason !== null) { + throw new Error("Complete Native build evidence must not include a partial allowance"); + } + return { + contract, + bundle, + scopeRef, + allowance: null, + allowanceRef: null, + findings: [], + unresolvedScopes: [] + }; + } + if (options.allowPartialScopeHash === void 0 || options.allowPartialScopeHash === null) { + return { + contract, + bundle, + scopeRef, + allowance: null, + allowanceRef: null, + findings: partialFindings(bundle.scope.unresolvedScopes), + unresolvedScopes: bundle.scope.unresolvedScopes + }; + } + if (!options.confirmed) { + throw new Error("Native partial verification requires explicit confirmation"); + } + const expectedScopeHash = partialScopeHash(options.allowPartialScopeHash); + if (expectedScopeHash !== bundle.scope.scopeHash) { + throw new Error("Native partial allowance does not match the current implementation scope"); + } + const allowance = buildNativePartialAllowance({ + change: options.state.name, + scopeBundle: bundle, + allowedScopeIds: bundle.scope.unresolvedScopes.map((scope) => scope.id), + reason: options.partialReason ?? "", + confirmedSummary: options.confirmedSummary ?? "", + sourceRevision: options.state.revision, + now: options.now + }); + const allowanceRef = nativeEvidenceRef("allowances", allowance.allowanceHash); + return { + contract, + bundle, + scopeRef, + allowance, + allowanceRef, + findings: [], + unresolvedScopes: bundle.scope.unresolvedScopes + }; +} +async function persistNativeBuildEvidence(options) { + const scopeRef = await writeNativeImplementationScope({ + paths: options.paths, + name: options.state.name, + bundle: options.preparation.bundle + }); + if (scopeRef !== options.preparation.scopeRef) { + throw new Error("Native implementation scope persistence ref changed"); + } + if (options.includeAllowance === false || options.preparation.allowance === null) return; + const allowanceRef = await writeNativePartialAllowance({ + paths: options.paths, + name: options.state.name, + allowance: options.preparation.allowance + }); + if (allowanceRef !== options.preparation.allowanceRef) { + throw new Error("Native partial allowance persistence ref changed"); + } +} + +// domains/comet-native/native-transitions.ts +function hasEvidenceRetreatExtras(evidence) { + return evidence.confirmed !== void 0 || evidence.artifacts !== void 0 || evidence.noCodeReason !== void 0 || evidence.allowPartialScopeHash !== void 0 || evidence.partialReason !== void 0 || evidence.verificationResult !== void 0 || evidence.verificationReport !== void 0 || evidence.verificationReceipt !== void 0 || evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0 || evidence.repairOverrideSignature !== void 0 || evidence.repairOverrideSummary !== void 0; +} +function repairFinding(decision) { + if (decision.reasonCode === "override-already-used") { + return { + code: "repair-override-exhausted", + message: `Native repair already used its override for signature: ${decision.signatureHash}` + }; + } + if (decision.disposition === "warn") { + return { + code: "repair-stagnation-warning", + message: `Native repair repeated the same failure signature: ${decision.signatureHash}` + }; + } + if (decision.disposition === "manual-stop") { + return { + code: "repair-stagnation-stop", + message: `Native repair stopped after repeated failure signature: ${decision.signatureHash}` + }; + } + return { + code: "repair-iteration-limit", + message: `Native repair reached its total iteration limit at signature: ${decision.signatureHash}` + }; +} +function validateNativeAdvanceEvidence(evidence) { + assertNativeTrajectoryText(evidence.summary, "Native transition summary"); + if (evidence.noCodeReason !== void 0) { + assertNativeTrajectoryText(evidence.noCodeReason, "Native transition no-code reason"); + } + if (evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0) { + normalizeNativeRepairFailureTokens({ + categories: evidence.repairFailureCategories, + failedCheckIds: evidence.repairFailedCheckIds + }); + } + if (evidence.repairOverrideSignature !== void 0 && !/^[a-f0-9]{64}$/u.test(evidence.repairOverrideSignature)) { + throw new Error("Native repair override signature must be a SHA-256 hash"); + } + if (evidence.repairOverrideSummary !== void 0) { + hashNativeRepairOverrideSummary(evidence.repairOverrideSummary); + } +} +function normalizeNativeAdvanceEvidence(evidence) { + return { + ...evidence, + summary: redactNativeCredentialText(evidence.summary), + ...evidence.noCodeReason === void 0 ? {} : { noCodeReason: redactNativeCredentialText(evidence.noCodeReason) }, + ...evidence.partialReason === void 0 ? {} : { partialReason: redactNativeCredentialText(evidence.partialReason) }, + ...evidence.repairOverrideSummary === void 0 ? {} : { repairOverrideSummary: redactNativeCredentialText(evidence.repairOverrideSummary) } + }; +} +function validateRepairEvidence(state, evidence) { + const hasFailureFacts = evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0; + if (hasFailureFacts && (state.phase !== "verify" || evidence.verificationResult !== "fail")) { + throw new Error("Native repair failure facts are only valid for a failed Verify outcome"); + } + const hasOverrideSignature = evidence.repairOverrideSignature !== void 0; + const hasOverrideSummary = evidence.repairOverrideSummary !== void 0; + if (hasOverrideSignature !== hasOverrideSummary) { + throw new Error("Native repair override signature and summary must be provided together"); + } + if ((hasOverrideSignature || hasOverrideSummary) && state.phase !== "build") { + throw new Error("Native repair override is only valid while leaving Build"); + } +} +async function retreatStaleNativeEvidence(options) { + if (hasEvidenceRetreatExtras(options.transition.evidence)) { + throw new Error("Native evidence retreat only accepts a transition summary"); + } + const previousPhase = options.state.phase; + if (previousPhase !== "verify" && previousPhase !== "archive" || options.run.currentStep !== previousPhase || options.run.pending !== null) { + throw new Error("Native Verify/Archive Run cannot retreat evidence safely"); + } + const evidenceIsFresh = previousPhase === "archive" ? ["complete", "partial"].includes( + (await inspectNativeVerificationFreshness({ + paths: options.transition.paths, + state: options.state, + now: options.transition.now + })).freshness + ) : (await inspectNativeImplementationScopeFreshness({ + paths: options.transition.paths, + state: options.state, + now: options.transition.now + })).freshness === "fresh"; + if (evidenceIsFresh) { + const findings = structureNativeFindings({ + paths: options.transition.paths, + state: options.state, + findings: [ + previousPhase === "archive" ? { + code: "archive-command-required", + message: "Current verification evidence is fresh; use Native Archive preview" + } : { + code: "verification-result-missing", + message: "Current implementation scope is fresh; complete Verify with a result and report" + } + ] + }); + return { + change: options.state, + previousPhase, + next: "manual", + nextCommand: previousPhase === "archive" ? `comet native archive ${options.state.name} --dry-run` : null, + findings, + continuation: nativeContinuation({ + state: options.state, + archiveReady: previousPhase === "archive", + clarificationMode: options.transition.clarificationMode + }) + }; + } + const nextState = { + ...options.state, + revision: options.state.revision + 1, + phase: "build", + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + }; + const nextRun = { + ...options.run, + currentStep: "build", + iteration: options.run.iteration + 1, + pending: null, + status: "running" + }; + const eventData = { + previousPhase, + nextPhase: "build", + evidenceHash: options.evidenceHash, + summary: options.transition.evidence.summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }; + const journal = await prepareNativeTransition({ + paths: options.transition.paths, + previousState: options.state, + nextState, + previousRun: options.run, + nextRun, + evidenceHash: options.evidenceHash, + eventData, + operation: "evidence-retreat", + now: options.transition.now, + transitionId: options.transition.transitionId + }); + await options.transition.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeTransitionLocked( + options.transition.paths, + options.state.name, + options.transition.hooks + ); + if (!persisted) throw new Error("Native evidence retreat journal disappeared before completion"); + return { + change: persisted, + previousPhase, + next: "auto", + nextCommand: null, + findings: [], + continuation: nativeContinuation({ + state: persisted, + clarificationMode: options.transition.clarificationMode + }) + }; +} +async function advanceNativeChange(options) { + const normalizedOptions = { + ...options, + evidence: normalizeNativeAdvanceEvidence(options.evidence) + }; + validateNativeAdvanceEvidence(normalizedOptions.evidence); + return withNativeMutationLock( + options.paths, + `advance ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `advance ${options.name}`, + () => advanceNativeChangeLocked(normalizedOptions) + ) + ); +} +async function advanceNativeChangeLocked(options) { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + const previousPhase = state.phase; + const changeDir = nativeChangeDir(options.paths, options.name); + const hash6 = nativeAdvanceEvidenceHash(options.evidence); + const existingRun = await readNativeRunState(changeDir); + if (existingRun) { + const trajectory = await readNativeTrajectory(changeDir, existingRun.trajectoryRef); + const last = trajectory.at(-1); + if (last?.type === "state_transitioned" && last.data.evidenceHash === hash6 && last.data.nextPhase === state.phase) { + const repair = Object.hasOwn(last.data, "repairStagnation") ? await inspectLatestNativeRepairDecision(options.paths, state) : null; + const repairFindings2 = repair && repair.disposition !== "continue" ? structureNativeFindings({ + paths: options.paths, + state, + findings: [repairFinding(repair)] + }) : []; + const stopped = repair?.disposition === "manual-stop" || repair?.disposition === "hard-stop"; + return { + change: state, + previousPhase: last.data.previousPhase ?? state.phase, + next: stopped ? "manual" : "auto", + nextCommand: stopped ? null : state.phase === "archive" ? `comet native archive ${state.name} --dry-run` : null, + findings: repairFindings2, + continuation: nativeContinuation({ + state, + findings: repairFindings2, + archiveReady: state.phase === "archive" && state.verification_result === "pass", + clarificationMode: options.clarificationMode + }), + ...repair ? { repair } : {} + }; + } + } + if (state.phase === "archive") { + if (!existingRun) throw new Error("Native Archive Run state is missing"); + return retreatStaleNativeEvidence({ + transition: options, + state, + run: existingRun, + evidenceHash: hash6 + }); + } + if (state.phase === "verify" && !hasEvidenceRetreatExtras(options.evidence)) { + if (!existingRun) throw new Error("Native Verify Run state is missing"); + const freshness = await inspectNativeImplementationScopeFreshness({ + paths: options.paths, + state, + now: options.now + }); + if (freshness.freshness !== "fresh") { + return retreatStaleNativeEvidence({ + transition: options, + state, + run: existingRun, + evidenceHash: hash6 + }); + } + } + const candidate = { + ...state, + spec_changes: await reconcileNativeSpecChanges(options.paths, state) + }; + validateRepairEvidence(state, options.evidence); + const guard = await inspectNativeGuard({ + paths: options.paths, + state: candidate, + evidence: options.evidence, + clarificationMode: options.clarificationMode + }); + if (!guard.valid) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: guard.findings + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + const shapeContract = state.phase === "shape" ? await collectNativeContractFiles({ + changeDir, + briefRef: candidate.brief, + specChanges: candidate.spec_changes + }) : null; + if (state.phase !== "build" && (options.evidence.allowPartialScopeHash !== void 0 || options.evidence.partialReason !== void 0)) { + throw new Error("Native partial scope allowance is only valid while leaving Build"); + } + const buildEvidence = state.phase === "build" ? await inspectNativeBuildEvidence({ + paths: options.paths, + state: candidate, + artifactRefs: options.evidence.artifacts ?? [], + noCodeReason: options.evidence.noCodeReason ?? null, + allowPartialScopeHash: options.evidence.allowPartialScopeHash ?? null, + partialReason: options.evidence.partialReason ?? null, + confirmedSummary: options.evidence.summary, + confirmed: options.evidence.confirmed ?? false, + now: options.now + }) : null; + if (buildEvidence && (state.approved_contract_hash ?? null) !== buildEvidence.contract.contract.contractHash && !options.evidence.confirmed) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: [ + { + code: "contract-changed-after-approval", + message: "Native contract changed after approval; re-confirm the current contract before leaving Build" + } + ] + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + const preparedScope = buildEvidence ? { + scopeHash: buildEvidence.bundle.scope.scopeHash, + scopeRef: buildEvidence.scopeRef, + complete: buildEvidence.bundle.scope.complete, + unresolvedScopeCount: buildEvidence.unresolvedScopes.length, + partialAllowanceRef: buildEvidence.allowanceRef, + acceptancePage: projectNativeAcceptancePage({ + criteria: buildEvidence.contract.contract.acceptance, + acceptanceHash: buildEvidence.contract.contract.acceptanceHash + }) + } : void 0; + if (buildEvidence && buildEvidence.findings.length > 0) { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence, + includeAllowance: false + }); + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: buildEvidence.findings + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }), + preparedScope + }; + } + let repairEventProjection = null; + let repairScopeHashForEvent = buildEvidence ? nativeRepairScopeHash(buildEvidence.bundle) : null; + if (state.phase === "build" && buildEvidence) { + const repairGuard = await inspectNativeRepairBuildGuard({ + paths: options.paths, + state, + currentImplementationScope: buildEvidence.bundle, + ...options.evidence.repairOverrideSignature && options.evidence.repairOverrideSummary ? { + override: { + expectedSignatureHash: options.evidence.repairOverrideSignature, + summary: options.evidence.repairOverrideSummary + } + } : {} + }); + if (repairGuard.disposition !== "proceed") { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence, + includeAllowance: false + }); + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: [ + repairGuard.disposition === "hard-stop" && repairGuard.overrideRecorded ? { + code: "repair-override-exhausted", + message: `Native repair already used its override for signature: ${repairGuard.signatureHash}` + } : repairFinding({ + disposition: repairGuard.disposition, + reasonCode: repairGuard.disposition === "hard-stop" ? "repair-iteration-limit" : "repeated-failure-stop", + signatureHash: repairGuard.signatureHash + }) + ] + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }), + preparedScope: preparedScope ? { ...preparedScope, partialAllowanceRef: null } : preparedScope + }; + } + repairEventProjection = repairGuard.eventProjection; + } + const verificationEvidence = state.phase === "verify" ? await inspectNativeVerificationEvidence({ + paths: options.paths, + state: candidate, + result: options.evidence.verificationResult, + reportRef: options.evidence.verificationReport, + receiptRef: options.evidence.verificationReceipt ?? null, + now: options.now + }) : null; + if (verificationEvidence && !verificationEvidence.ready) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: verificationEvidence.findingCodes.map((code) => ({ + code, + message: `Native verification evidence is not current: ${code}` + })) + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + let repairDecision = null; + if (state.phase === "verify" && options.evidence.verificationResult === "fail" && verificationEvidence?.ready && verificationEvidence.envelope) { + const repairResult = await inspectNativeRepairFailureForTransition({ + paths: options.paths, + state, + envelope: verificationEvidence.envelope, + ...options.evidence.repairFailureCategories ? { categories: options.evidence.repairFailureCategories } : {}, + ...options.evidence.repairFailedCheckIds ? { failedCheckIds: options.evidence.repairFailedCheckIds } : {} + }); + repairEventProjection = repairResult.eventProjection; + repairScopeHashForEvent = repairResult.facts.implementationScopeHash; + repairDecision = projectNativeRepairDecision(repairResult); + } + let run = existingRun; + if (!run) { + if (state.run_id !== null || state.phase !== "shape") { + throw new Error("Native Run state is missing or inconsistent"); + } + run = startNativeRun( + NATIVE_RUNTIME_PACKAGE, + options.runId?.() ?? randomUUID11(), + NATIVE_RUNTIME_HASH + ); + } + if (run.currentStep !== state.phase) { + throw new Error(`Native Run step ${run.currentStep ?? "(none)"} does not match ${state.phase}`); + } + const decision = decideWithResolver( + NATIVE_RUNTIME_PACKAGE, + run, + /* @__PURE__ */ new Set(), + nativePhaseResolver, + void 0 + ); + if (!decision.action) throw new Error(decision.reason ?? "Native runtime produced no action"); + const advanced = recordOutcomeWithResolver( + NATIVE_RUNTIME_PACKAGE, + decision.state, + { + actionId: decision.action.id, + status: "succeeded", + summary: options.evidence.summary, + state: options.evidence.verificationResult ? { verification_result: options.evidence.verificationResult } : void 0 + }, + nativePhaseResolver, + void 0 + ); + if (!advanced.currentStep) throw new Error("Archive completion must use the archive command"); + const updated = { + ...candidate, + revision: state.revision + 1, + phase: advanced.currentStep, + approval: options.evidence.confirmed ? "confirmed" : state.approval, + approved_contract_hash: state.phase === "shape" ? shapeContract.contract.contractHash : state.phase === "build" && options.evidence.confirmed ? buildEvidence.contract.contract.contractHash : state.approved_contract_hash ?? null, + run_id: run.runId, + ...state.phase === "build" ? { + verification_result: "pending", + verification_report: null, + implementation_scope: buildEvidence.scopeRef, + partial_allowance: buildEvidence.allowanceRef, + verification_evidence: null + } : {}, + ...state.phase === "verify" ? { + verification_result: options.evidence.verificationResult, + verification_report: verificationEvidence.envelope.reportRef, + verification_evidence: verificationEvidence.evidenceRef + } : {} + }; + const eventData = { + previousPhase, + nextPhase: updated.phase, + evidenceHash: hash6, + summary: options.evidence.summary, + artifacts: options.evidence.artifacts ?? [], + noCodeReason: options.evidence.noCodeReason ?? null, + verificationResult: options.evidence.verificationResult ?? null, + ...(state.phase === "build" || state.phase === "verify") && (state.phase === "build" ? buildEvidence.bundle.scope.scopeHash : verificationEvidence.envelope.implementationScopeHash) ? { + implementationScopeHash: state.phase === "build" ? buildEvidence.bundle.scope.scopeHash : verificationEvidence.envelope.implementationScopeHash + } : {}, + ...repairScopeHashForEvent ? { repairScopeHash: repairScopeHashForEvent } : {}, + ...repairEventProjection ? { repairStagnation: repairEventProjection } : {} + }; + if (state.phase === "build" && buildEvidence) { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence + }); + } + if (state.phase === "verify" && verificationEvidence) { + await persistNativeVerificationEvidence({ + paths: options.paths, + state, + preparation: verificationEvidence + }); + } + const journal = await prepareNativeTransition({ + paths: options.paths, + previousState: state, + nextState: updated, + previousRun: existingRun, + nextRun: advanced, + evidenceHash: hash6, + eventData, + now: options.now, + transitionId: options.transitionId + }); + await options.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeTransitionLocked( + options.paths, + options.name, + options.hooks + ); + if (!persisted) throw new Error("Native transition journal disappeared before completion"); + const repairFindings = repairDecision && repairDecision.disposition !== "continue" ? structureNativeFindings({ + paths: options.paths, + state: persisted, + findings: [repairFinding(repairDecision)] + }) : []; + const repairStopped = repairDecision?.disposition === "manual-stop" || repairDecision?.disposition === "hard-stop"; + return { + change: persisted, + previousPhase, + next: repairStopped ? "manual" : "auto", + nextCommand: repairStopped ? null : persisted.phase === "archive" ? `comet native archive ${persisted.name} --dry-run` : null, + findings: repairFindings, + continuation: nativeContinuation({ + state: persisted, + findings: repairFindings, + archiveReady: persisted.phase === "archive" && persisted.verification_result === "pass", + clarificationMode: options.clarificationMode + }), + ...preparedScope ? { preparedScope } : {}, + ...repairDecision ? { repair: repairDecision } : {} + }; +} + +// domains/comet-native/native-hook-guard.ts +import { promises as fs33 } from "fs"; +import path46 from "path"; + +// domains/comet-entry/hook-adapter.ts +import { readFileSync } from "fs"; +var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "applypatch", + "create", + "createfile", + "deletefile", + "edit", + "editfile", + "patch", + "strreplaceeditor", + "write", + "writefile", + "writefiletool" +]); +var NON_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "glob", + "grep", + "listfiles", + "read", + "readfile", + "search", + "view" +]); +var SINGULAR_PATH_KEYS = ["file_path", "filePath", "path", "target_file", "targetFile"]; +var PLURAL_PATH_KEYS = ["file_paths", "filePaths", "paths", "files", "targets"]; +var NESTED_TARGET_KEYS = ["operations", "edits"]; +var PATCH_KEYS = ["patch", "diff", "patchText", "patch_text", "changes"]; +function isRecord2(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function normalizedToolName(value) { + return value.toLowerCase().replace(/[^a-z0-9]+/gu, ""); +} +function readToolName(input) { + for (const key of ["tool_name", "toolName", "tool", "name"]) { + const value = input[key]; + if (typeof value === "string" && value.trim()) return value.trim(); + } + return null; +} +function parseJsonValue(value) { + if (typeof value !== "string") return value; + const source = value.trim(); + if (!source.startsWith("{") && !source.startsWith("[")) return value; + try { + return JSON.parse(source); + } catch { + return value; + } +} +function readToolArguments(input) { + for (const key of ["tool_input", "toolInput", "toolArgs", "tool_args", "arguments"]) { + if (input[key] !== void 0) return parseJsonValue(input[key]); + } + return input; +} +function patchTargets(source) { + const targets = []; + const patterns = [ + /^\*\*\* (?:Add|Update|Delete) File:\s+(.+?)\s*$/gmu, + /^\+\+\+\s+(?:b\/)?(.+?)\s*$/gmu + ]; + for (const pattern of patterns) { + for (const match of source.matchAll(pattern)) { + const target = match[1]?.trim(); + if (target && target !== "/dev/null") targets.push(target); + } + } + return targets; +} +function addTarget(targets, value) { + if (typeof value === "string") { + const target = value.trim(); + if (target) targets.push(target); + return; + } + if (Array.isArray(value)) { + for (const entry2 of value) addTarget(targets, entry2); + return; + } + if (!isRecord2(value)) return; + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, value[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, value[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, value[key]); +} +function collectTargets(input, args) { + const targets = []; + const records = [args, input].filter(isRecord2); + for (const record8 of records) { + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, record8[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, record8[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, record8[key]); + for (const key of PATCH_KEYS) { + const value = record8[key]; + if (typeof value === "string") targets.push(...patchTargets(value)); + } + } + if (typeof args === "string") targets.push(...patchTargets(args)); + return [...new Set(targets)]; +} +function parseCometHookRequest(source, filePath) { + if (filePath?.trim()) { + return { intent: "write", targets: [filePath.trim()], toolName: null }; + } + if (!source.trim()) return { intent: "unknown", targets: [], toolName: null }; + let input; + try { + input = JSON.parse(source); + } catch { + return { intent: "unknown", targets: [], toolName: null }; + } + if (!isRecord2(input)) return { intent: "unknown", targets: [], toolName: null }; + const toolName = readToolName(input); + const targets = collectTargets(input, readToolArguments(input)); + if (toolName && WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName + }; + } + if (toolName && NON_WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { intent: "non-write", targets: [], toolName }; + } + if (toolName) return { intent: "unknown", targets, toolName }; + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName: null + }; +} +function readCometHookRequest() { + const filePath = process.env.FILE_PATH; + if (filePath?.trim()) return parseCometHookRequest("", filePath); + if (process.stdin.isTTY) return parseCometHookRequest("", filePath); + try { + return parseCometHookRequest(readFileSync(0, "utf8"), filePath); + } catch { + return parseCometHookRequest("", filePath); + } +} + +// domains/comet-native/native-hook-guard.ts +function isWithin(parent, target) { + const relative = path46.relative(parent, target); + return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative); +} +function requestTargetsAreControlOnly(projectRoot, nativeRoot, request) { + return request.targets.length > 0 && request.targets.every((targetPath) => { + const target = path46.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) return true; + const relative = path46.relative(projectRoot, target).replaceAll("\\", "/"); + return relative === ".comet/config.yaml" || isWithin(nativeRoot, target); + }); +} +async function activeNativeContext(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (!config || !(config.workflows ?? [config.default_workflow]).includes("native")) return null; + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + let entries; + try { + entries = await fs33.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return { paths, changes: [] }; + throw error; + } + const changes = []; + for (const entry2 of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + const state = await readNativeChange(paths, entry2.name); + if (!state.archived) changes.push(state); + } + return { paths, changes }; +} +async function readNativeHookRequest() { + const { intent, targets } = await readCometHookRequest(); + return { intent, targets }; +} +async function inspectNativeHookGuard(projectRoot, request, selectedChangeName) { + const context = await activeNativeContext(projectRoot); + if (!context) return { allowed: true, reason: "Native workflow is not enabled" }; + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (context.changes.length === 0) { + return { + allowed: true, + reason: requestTargetsAreControlOnly(projectRoot, context.paths.nativeRoot, request) ? "Native control artifact write" : "No Native changes exist" + }; + } + let change; + if (selectedChangeName) { + change = context.changes.find((candidate) => candidate.name === selectedChangeName); + if (!change) { + return { + allowed: false, + reason: `Selected Native change ${selectedChangeName} is missing or archived; resume /comet-native before retrying`, + workflow: "native", + change: selectedChangeName + }; + } + } else if (context.changes.length === 1) { + change = context.changes[0]; + } else { + const selectedName2 = await resolveSelectedNativeChange(context.paths); + change = context.changes.find((candidate) => candidate.name === selectedName2); + if (!change) { + return { + allowed: false, + reason: "Multiple Native changes are active; select the change to resume before writing code", + workflow: "native" + }; + } + } + if (change.phase === "build") { + return { + allowed: true, + reason: "Native change is in Build", + workflow: "native", + phase: change.phase, + change: change.name + }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + return { + allowed: false, + reason: `Hook write target could not be determined while Native change ${change.name} is in ${change.phase}; resume /comet-native before retrying`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + let controlTarget = false; + let externalTarget = false; + for (const targetPath of request.targets) { + const target = path46.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) { + externalTarget = true; + continue; + } + const relative = path46.relative(projectRoot, target).replaceAll("\\", "/"); + if (relative === ".comet/config.yaml" || isWithin(context.paths.nativeRoot, target)) { + controlTarget = true; + continue; + } + return { + allowed: false, + reason: `Native change ${change.name} is in ${change.phase}; implementation writes are only allowed in build. Resume /comet-native to continue safely`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + return { + allowed: true, + reason: controlTarget ? "Native control artifact write" : externalTarget ? "Write target is outside the guarded project" : "No guarded write target was provided", + workflow: "native", + phase: change.phase, + change: change.name + }; +} + +// domains/comet-native/native-cli.ts +var NATIVE_SHOW_MAX_SERIALIZED_BYTES = 10 * 1024 * 1024; +var NativeUsageError = class extends Error { +}; +var USAGE = `Usage: comet native [options] + +Commands: + hook-guard [--hook-output copilot] + init [--root ] [--language en|zh-CN] + root show + root move + new [--language en|zh-CN] + spec remove + spec rebase --summary + list [--cursor ] + show + status [] [--cursor ] [--details [--acceptance-cursor ]] + select + checkpoint --summary --next-action [--artifact ] [--expect-revision ] + check + evidence format [--entries ] + next --summary [--confirmed] [--artifact ] [--no-code-reason ] [--allow-partial-scope --partial-reason ] [--result pass|fail] [--report ] [--receipt ] [--failure-category ] [--failed-check ] [--override-repair --override-summary ] + archive --dry-run + archive --expect-preflight + doctor [] [--repair] [--strategy continue|rollback] +`; +function takeFlag(args, name) { + const indexes = args.flatMap((value, index) => value === name ? [index] : []); + if (indexes.length > 1) throw new NativeUsageError(`${name} may only be provided once`); + if (indexes.length === 0) return false; + args.splice(indexes[0], 1); + return true; +} +function takeOption(args, name) { + const indexes = args.flatMap((value2, index2) => value2 === name ? [index2] : []); + if (indexes.length > 1) throw new NativeUsageError(`${name} may only be provided once`); + if (indexes.length === 0) return void 0; + const index = indexes[0]; + const value = args[index + 1]; + if (value === void 0 || value.startsWith("--")) { + throw new NativeUsageError(`${name} requires a value`); + } + args.splice(index, 2); + return value; +} +function takeMany(args, name) { + const values = []; + for (let index = 0; index < args.length; ) { + if (args[index] !== name) { + index += 1; + continue; + } + const value = args[index + 1]; + if (value === void 0 || value.startsWith("--")) { + throw new NativeUsageError(`${name} requires a value`); + } + values.push(value); + args.splice(index, 2); + } + return values; +} +function assertNoArguments(args) { + if (args.length > 0) throw new NativeUsageError(`Unexpected argument: ${args[0]}`); +} +function requiredPositional(args, label) { + const value = args.shift(); + if (!value || value.startsWith("--")) throw new NativeUsageError(`${label} is required`); + return value; +} +function languageOption(args, fallback = "en") { + const language = takeOption(args, "--language") ?? fallback; + if (language !== "en" && language !== "zh-CN") { + throw new NativeUsageError("--language must be en or zh-CN"); + } + return language; +} +function revisionOption(args) { + const value = takeOption(args, "--expect-revision"); + if (value === void 0) return void 0; + if (!/^[1-9]\d*$/u.test(value) || !Number.isSafeInteger(Number(value))) { + throw new NativeUsageError("--expect-revision must be a positive integer"); + } + return Number(value); +} +async function projectRootFrom(explicit) { + return explicit ? path47.resolve(explicit) : discoverNativeProject(process.cwd()); +} +async function configuredPaths(projectRoot) { + const resolved = await resolveNativeProject({ + startPath: projectRoot, + allowMissingConfig: false + }); + return { config: resolved.config, paths: resolved.paths }; +} +async function doctorPaths(projectRoot) { + const config = await readProjectConfig(projectRoot); + return nativeProjectPaths(projectRoot, config?.native.artifact_root ?? "docs"); +} +function success(command, data, text) { + return { command, exitCode: 0, data, text: text ?? JSON.stringify(data, null, 2) + "\n" }; +} +async function readBoundedEvidenceFile(filePath, maxBytes) { + try { + const { bytes } = await readFileRaceSafe(filePath, maxBytes, { + label: "Acceptance evidence entries file" + }); + return bytes.toString("utf8"); + } catch (error) { + if (error instanceof RaceSafeReadError) { + if (error.reason === "not-regular-file") { + throw new Error(`Acceptance evidence entries path is not a regular file: ${filePath}`, { + cause: error + }); + } + if (error.reason === "too-large") { + throw new Error(`Acceptance evidence entries file exceeds ${maxBytes} bytes: ${filePath}`, { + cause: error + }); + } + throw new Error(`Acceptance evidence entries file changed while reading: ${filePath}`, { + cause: error + }); + } + throw error; + } +} +async function readBoundedEvidenceStdin(maxBytes) { + const chunks = []; + let total = 0; + for await (const chunk of process.stdin) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)); + total += buffer.byteLength; + if (total > maxBytes) { + throw new Error(`Acceptance evidence entries on stdin exceed ${maxBytes} bytes`); + } + chunks.push(buffer); + } + return Buffer.concat(chunks).toString("utf8"); +} +async function dispatch(rawArgs, explicitProjectRoot) { + if (rawArgs.length === 0 || rawArgs[0] === "--help" || rawArgs[0] === "help") { + return { command: rawArgs[0] ?? null, exitCode: 0, data: { usage: USAGE }, text: USAGE }; + } + const command = rawArgs.shift(); + const projectRoot = await projectRootFrom(explicitProjectRoot); + if (command === "hook-guard") { + const hookOutput = takeOption(rawArgs, "--hook-output"); + if (hookOutput !== void 0 && hookOutput !== "copilot") { + throw new NativeUsageError("--hook-output must be copilot"); + } + assertNoArguments(rawArgs); + const result2 = await inspectNativeHookGuard(projectRoot, await readNativeHookRequest()); + if (hookOutput === "copilot") { + return { + command, + exitCode: 0, + data: result2, + text: result2.allowed ? "{}\n" : `${JSON.stringify({ + permissionDecision: "deny", + permissionDecisionReason: result2.reason + })} +` + }; + } + return result2.allowed ? { command, exitCode: 0, data: result2 } : { + command, + exitCode: 2, + data: result2, + error: { code: "blocked", message: result2.reason } + }; + } + if (command === "init") { + const requestedRoot = takeOption(rawArgs, "--root"); + const existing = await readProjectConfig(projectRoot); + const language = languageOption(rawArgs, existing?.native.language ?? "en"); + assertNoArguments(rawArgs); + if (existing?.native.pending_root_move) { + throw new Error(`Native root move ${existing.native.pending_root_move.id} is incomplete`); + } + const artifactRoot = normalizeArtifactRootRef( + requestedRoot ?? existing?.native.artifact_root ?? "docs" + ); + if (existing && requestedRoot && existing.native.artifact_root !== artifactRoot) { + throw new Error( + `Configured Native artifact root is ${existing.native.artifact_root}; refusing conflicting root ${artifactRoot}` + ); + } + const config = existing ? { ...existing, native: { ...existing.native, language } } : defaultProjectConfig(artifactRoot, language); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + await ensureNativeDirectories(paths); + await writeProjectConfig(projectRoot, config); + return success( + "init", + { + projectRoot, + artifactRoot: config.native.artifact_root, + nativeRoot: paths.nativeRoot, + language + }, + `Initialized Comet Native at ${paths.nativeRoot} +` + ); + } + if (command === "root") { + const subcommand = requiredPositional(rawArgs, "root subcommand"); + if (subcommand === "show") { + assertNoArguments(rawArgs); + const config = await readProjectConfig(projectRoot); + if (!config) throw new Error(".comet/config.yaml was not found"); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + return success("root show", { + projectRoot, + artifactRoot: config.native.artifact_root, + language: config.native.language, + nativeRoot: paths.nativeRoot, + pendingRootMove: config.native.pending_root_move ?? null + }); + } + if (subcommand === "move") { + const target = requiredPositional(rawArgs, "artifact root"); + assertNoArguments(rawArgs); + const result2 = await moveNativeRoot({ projectRoot, toArtifactRoot: target }); + return success("root move", result2, `Moved Comet Native to ${result2.toNativeRoot} +`); + } + throw new NativeUsageError(`Unknown root command: ${subcommand}`); + } + if (command === "new") { + const name = requiredPositional(rawArgs, "change name"); + let config = await readProjectConfig(projectRoot); + const language = languageOption(rawArgs, config?.native.language ?? "en"); + assertNoArguments(rawArgs); + const shouldWriteConfig = config === null; + if (!config) { + config = defaultProjectConfig("docs", language); + } + if (config.native.pending_root_move) { + throw new Error(`Native root move ${config.native.pending_root_move.id} is incomplete`); + } + if (shouldWriteConfig) await writeProjectConfig(projectRoot, config); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + await ensureNativeDirectories(paths); + const state = await createNativeChange({ paths, name, language }); + await selectNativeChange(paths, state.name); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "new", + { ...state, continuation: status.continuation }, + `Created Native change ${state.name} +` + ); + } + if (command === "spec") { + const subcommand = requiredPositional(rawArgs, "spec subcommand"); + if (subcommand === "remove") { + const name = requiredPositional(rawArgs, "change name"); + const capability = requiredPositional(rawArgs, "capability"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const state = await markNativeSpecRemoval(paths, name, capability); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "spec remove", + { ...state, continuation: status.continuation }, + `Marked Native capability ${capability} for removal in ${name} +` + ); + } + if (subcommand === "rebase") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const state = await rebaseNativeSpecChanges({ paths, name, summary }); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "spec rebase", + { ...state, continuation: status.continuation }, + `Rebased Native specs for ${name} +` + ); + } + throw new NativeUsageError(`Unknown spec command: ${subcommand}`); + } + if (command === "list") { + const cursor = takeOption(rawArgs, "--cursor"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const page = await listNativeStatusPage(paths, { + ...cursor ? { cursor } : {}, + clarificationMode: config.native.clarification_mode + }); + return success("list", page); + } + if (command === "show") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + return success("show", { + name, + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + migrationRequired: true, + message: inspection.message + }); + } + if (inspection.status !== "current" || !inspection.state) { + throw new NativeRuntimeCompatibilityError( + inspection.schema, + inspection.minimumRuntimeVersion + ); + } + const state = inspection.state; + const changeDir = nativeChangeDir(paths, name); + const proposedSpecs = await readNativeProposedSpecs(paths, name); + const brief = await readNativeBoundedTextFile({ + root: changeDir, + ref: state.brief, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + const payload = { + state, + brief: brief.text, + proposedSpecs + }; + if (Buffer.byteLength(JSON.stringify(payload), "utf8") > NATIVE_SHOW_MAX_SERIALIZED_BYTES) { + throw new Error("Native show output exceeds its serialized byte budget"); + } + return success("show", payload); + } + if (command === "status") { + const details = takeFlag(rawArgs, "--details"); + const cursor = takeOption(rawArgs, "--cursor"); + const acceptanceCursor2 = takeOption(rawArgs, "--acceptance-cursor"); + const name = rawArgs[0]?.startsWith("--") ? void 0 : rawArgs.shift(); + if (details && !name) throw new NativeUsageError("status --details requires a change name"); + if (cursor && name) throw new NativeUsageError("--cursor is only valid for status lists"); + if (cursor && details) throw new NativeUsageError("--cursor cannot be combined with --details"); + if (acceptanceCursor2 && !details) { + throw new NativeUsageError("--acceptance-cursor requires status --details"); + } + if (acceptanceCursor2 && !name) { + throw new NativeUsageError("--acceptance-cursor requires a change name"); + } + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const data = name ? await inspectNativeStatus(paths, name, { + details, + ...acceptanceCursor2 ? { acceptanceCursor: acceptanceCursor2 } : {}, + clarificationMode: config.native.clarification_mode + }) : await listNativeStatusPage(paths, { + ...cursor ? { cursor } : {}, + clarificationMode: config.native.clarification_mode + }); + return success("status", data); + } + if (command === "select") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + await selectNativeChange(paths, name); + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "select", + { selected: name, continuation: status.continuation }, + `Selected Native change ${name} +` + ); + } + if (command === "checkpoint") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + const nextAction = takeOption(rawArgs, "--next-action"); + if (!nextAction) throw new NativeUsageError("--next-action is required"); + const artifacts = takeMany(rawArgs, "--artifact"); + const expectedRevision = revisionOption(rawArgs); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const result2 = await checkpointNativeChange({ + paths, + name, + summary, + nextAction, + artifacts, + expectedRevision + }); + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + const manifestRef = path47.relative( + paths.projectRoot, + path47.join(nativeChangeDir(paths, name), ...result2.checkpoint.manifestRef.split("/")) + ).replaceAll("\\", "/"); + return success("checkpoint", { + ...result2, + checkpoint: { ...result2.checkpoint, manifestRef }, + continuation: status.continuation + }); + } + if (command === "check") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + const checked = await checkNativeChange({ paths, name }); + const data = { + ref: checked.ref, + hash: checked.receipt.receiptHash, + status: checked.receipt.status, + checker: checked.receipt.checker, + counts: checked.receipt.counts, + issues: checked.receipt.issues, + issuesTruncated: checked.receipt.issuesTruncated, + stale: checked.receipt.stale, + staleReasons: checked.receipt.staleReasons, + startedAt: checked.receipt.startedAt, + endedAt: checked.receipt.endedAt, + sourceRevision: checked.receipt.sourceRevision + }; + const passed = checked.receipt.status === "passed" && !checked.receipt.stale; + return { + command: "check", + exitCode: passed ? 0 : 1, + data, + text: `Native check ${passed ? "passed" : "failed"}: ${checked.ref} +` + }; + } + if (command === "evidence") { + const subcommand = requiredPositional(rawArgs, "evidence subcommand"); + if (subcommand === "format") { + const entriesPath = takeOption(rawArgs, "--entries"); + assertNoArguments(rawArgs); + let raw; + if (entriesPath) { + raw = await readBoundedEvidenceFile( + path47.resolve(entriesPath), + MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES + ); + } else { + if (process.stdin.isTTY) { + throw new NativeUsageError( + "evidence format requires acceptance evidence entries JSON on stdin, or --entries " + ); + } + raw = await readBoundedEvidenceStdin(MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES); + } + let entries; + try { + entries = JSON.parse(raw); + } catch (error) { + throw new Error( + `Acceptance evidence entries must be valid JSON: ${error.message}`, + { cause: error } + ); + } + if (!Array.isArray(entries)) { + throw new Error("Acceptance evidence entries must be a JSON array"); + } + const block = serializeNativeVerificationMachineBlock(entries); + return success("evidence format", { block }, `${block} +`); + } + throw new NativeUsageError(`Unknown evidence command: ${subcommand}`); + } + if (command === "next") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + const confirmed = takeFlag(rawArgs, "--confirmed"); + const artifacts = takeMany(rawArgs, "--artifact"); + const noCodeReason = takeOption(rawArgs, "--no-code-reason"); + const allowPartialScopeHash = takeOption(rawArgs, "--allow-partial-scope"); + const partialReason = takeOption(rawArgs, "--partial-reason"); + const verificationResult = takeOption(rawArgs, "--result"); + const verificationReport = takeOption(rawArgs, "--report"); + const verificationReceipt = takeOption(rawArgs, "--receipt"); + const repairFailureCategories = takeMany(rawArgs, "--failure-category"); + const repairFailedCheckIds = takeMany(rawArgs, "--failed-check"); + const repairOverrideSignature = takeOption(rawArgs, "--override-repair"); + const repairOverrideSummary = takeOption(rawArgs, "--override-summary"); + if (verificationResult !== void 0 && verificationResult !== "pass" && verificationResult !== "fail") { + throw new NativeUsageError("--result must be pass or fail"); + } + if (allowPartialScopeHash === void 0 !== (partialReason === void 0)) { + throw new NativeUsageError( + "--allow-partial-scope and --partial-reason must be provided together" + ); + } + if (allowPartialScopeHash && !/^[a-f0-9]{64}$/u.test(allowPartialScopeHash)) { + throw new NativeUsageError("--allow-partial-scope must be a SHA-256 hash"); + } + if (allowPartialScopeHash && !confirmed) { + throw new NativeUsageError("--allow-partial-scope requires --confirmed"); + } + if ((repairFailureCategories.length > 0 || repairFailedCheckIds.length > 0) && verificationResult !== "fail") { + throw new NativeUsageError("--failure-category and --failed-check require --result fail"); + } + if (verificationReceipt && verificationResult === void 0) { + throw new NativeUsageError("--receipt requires --result"); + } + if (repairOverrideSignature === void 0 !== (repairOverrideSummary === void 0)) { + throw new NativeUsageError( + "--override-repair and --override-summary must be provided together" + ); + } + if (repairOverrideSignature && !/^[a-f0-9]{64}$/u.test(repairOverrideSignature)) { + throw new NativeUsageError("--override-repair must be a SHA-256 hash"); + } + if (repairOverrideSignature && verificationResult !== void 0) { + throw new NativeUsageError("--override-repair cannot be combined with --result"); + } + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const evidence = { + summary, + ...confirmed ? { confirmed: true } : {}, + ...artifacts.length > 0 ? { artifacts } : {}, + ...noCodeReason ? { noCodeReason } : {}, + ...allowPartialScopeHash ? { allowPartialScopeHash } : {}, + ...partialReason ? { partialReason } : {}, + ...verificationResult ? { verificationResult } : {}, + ...verificationReport ? { verificationReport } : {}, + ...verificationReceipt ? { verificationReceipt } : {}, + ...repairFailureCategories.length > 0 ? { repairFailureCategories } : {}, + ...repairFailedCheckIds.length > 0 ? { repairFailedCheckIds } : {}, + ...repairOverrideSignature ? { repairOverrideSignature } : {}, + ...repairOverrideSummary ? { repairOverrideSummary } : {} + }; + const result2 = await advanceNativeChange({ + paths, + name, + evidence, + clarificationMode: config.native.clarification_mode + }); + if (result2.next === "manual") { + const repairBlocked = result2.repair?.disposition === "manual-stop" || result2.repair?.disposition === "hard-stop" || result2.findings.some( + (finding) => [ + "repair-stagnation-stop", + "repair-iteration-limit", + "repair-override-exhausted" + ].includes(finding.code) + ); + return { + command: "next", + exitCode: repairBlocked ? 75 : 65, + data: result2, + error: { + code: repairBlocked ? "blocked" : "invalid-data", + message: result2.findings[0]?.message ?? "Native phase guard failed" + } + }; + } + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + return success("next", { ...result2, continuation: status.continuation }); + } + if (command === "archive") { + const name = requiredPositional(rawArgs, "change name"); + const dryRun = takeFlag(rawArgs, "--dry-run"); + const expectedPreflightHash = takeOption(rawArgs, "--expect-preflight"); + if (dryRun && expectedPreflightHash) { + throw new NativeUsageError("--dry-run and --expect-preflight cannot be combined"); + } + if (!dryRun && !expectedPreflightHash) { + throw new NativeUsageError("archive requires --dry-run or --expect-preflight "); + } + if (expectedPreflightHash && !/^[a-f0-9]{64}$/u.test(expectedPreflightHash)) { + throw new NativeUsageError("--expect-preflight must be a SHA-256 hash"); + } + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + if (dryRun) { + const preview = await inspectNativeArchivePreflight({ paths, name }); + return success( + "archive --dry-run", + preview, + `Native Archive preview ${preview.preflightHash}: ${preview.ready ? "ready" : "blocked"} +` + ); + } + const state = await readNativeChange(paths, name); + const result2 = await archiveNativeChange({ + paths, + name, + expectedPreflightHash + }); + return success( + "archive", + { ...result2, continuation: nativeContinuation({ state, done: true }) }, + `Archived Native change ${name} to ${result2.archiveDir} +` + ); + } + if (command === "doctor") { + const repair = takeFlag(rawArgs, "--repair"); + const recoveryStrategy = takeOption(rawArgs, "--strategy"); + if (recoveryStrategy !== void 0 && recoveryStrategy !== "continue" && recoveryStrategy !== "rollback") { + throw new NativeUsageError("--strategy must be continue or rollback"); + } + const name = rawArgs[0]?.startsWith("--") ? void 0 : rawArgs.shift(); + assertNoArguments(rawArgs); + const paths = await doctorPaths(projectRoot); + const result2 = await doctorNativeProject({ + paths, + ...name ? { name } : {}, + repair, + ...recoveryStrategy ? { recoveryStrategy } : {} + }); + return result2.healthy ? success("doctor", result2) : { + command: "doctor", + exitCode: 65, + data: result2, + error: { code: "invalid-data", message: "Native project needs attention" } + }; + } + throw new NativeUsageError(`Unknown Native command: ${command}`); +} +function errorResult(command, error) { + if (error instanceof NativeUsageError) { + return { + command, + exitCode: 64, + error: { code: "usage", message: error.message } + }; + } + if (error instanceof NativeSpecConflictError) { + return { + command, + exitCode: 73, + data: { + capability: error.capability, + expectedHash: error.expectedHash, + actualHash: error.actualHash, + canonicalPath: error.canonicalPath + }, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeArchivePreflightError) { + return { + command, + exitCode: 73, + data: error.preflight, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeChangeRevisionConflictError) { + return { + command, + exitCode: 73, + data: { + change: error.change, + expectedRevision: error.expectedRevision, + actualRevision: error.actualRevision, + outcome: "revision-conflict" + }, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeBaselineIncompleteError) { + return { + command, + exitCode: 65, + data: { + change: error.change, + complete: false, + omittedCount: error.omittedCount, + omittedByReason: error.omittedByReason, + samplePaths: error.samplePaths, + sampleTruncated: error.sampleTruncated, + effectiveLimits: error.effectiveLimits, + policyHash: error.policyHash, + configPath: ".comet/config.yaml", + supportedFixes: [ + "increase native.snapshot.max_total_bytes or native.snapshot.max_duration_ms", + "add an explicit native.snapshot.exclude pattern for data outside implementation scope" + ], + requiredAction: "resolve-native-baseline" + }, + error: { code: "baseline-incomplete", message: error.message } + }; + } + if (error instanceof Error) { + const systemCode = error.code; + if (systemCode && (/* @__PURE__ */ new Set(["EACCES", "EPERM", "EIO", "EMFILE", "ENFILE", "ENOSPC", "EROFS"])).has(systemCode)) { + return { + command, + exitCode: 70, + error: { code: "internal", message: error.message } + }; + } + const conflict = /\b(lock|transaction|conflict|occupied|incomplete|recovery)\b/iu.test( + error.message + ); + return { + command, + exitCode: conflict ? 73 : 65, + error: { code: conflict ? "conflict" : "invalid-data", message: error.message } + }; + } + return { + command, + exitCode: 70, + error: { code: "internal", message: String(error) } + }; +} +function render(result2, json) { + if (json) { + return { + exitCode: result2.exitCode, + stdout: JSON.stringify({ + command: result2.command, + exitCode: result2.exitCode, + ...result2.data === void 0 ? {} : { data: result2.data }, + ...result2.error === void 0 ? {} : { error: result2.error } + }) + "\n" + }; + } + if (result2.error) { + return { exitCode: result2.exitCode, stderr: result2.error.message }; + } + return { exitCode: result2.exitCode, stdout: result2.text }; +} +async function runNativeCli(argv) { + const args = [...argv]; + const json = args.includes("--json"); + let explicitProjectRoot; + let command = args[0] ?? null; + try { + takeFlag(args, "--json"); + explicitProjectRoot = takeOption(args, "--project-root"); + command = args[0] ?? null; + return render(await dispatch(args, explicitProjectRoot), json); + } catch (error) { + return render(errorResult(command, error), json); + } +} + +// domains/comet-native/native-cli-entry.ts +async function main(argv = process.argv.slice(2)) { + const result2 = await runNativeCli(argv); + if (result2.stdout) process.stdout.write(result2.stdout); + if (result2.stderr) { + process.stderr.write(result2.stderr + (result2.stderr.endsWith("\n") ? "" : "\n")); + } + return result2.exitCode; +} +var entry = process.argv[1]; +if (entry && import.meta.url === pathToFileURL(entry).href) { + void main().then((exitCode) => { + process.exitCode = exitCode; + }); +} +export { + main +}; diff --git a/.agents/skills/comet/SKILL.md b/.agents/skills/comet/SKILL.md new file mode 100644 index 0000000..e0809a1 --- /dev/null +++ b/.agents/skills/comet/SKILL.md @@ -0,0 +1,28 @@ +--- +name: comet +description: "当用户调用 /comet 或需要恢复 active Comet change 时,按项目配置路由到永久的 Comet Native 或 Comet Classic 入口。" +--- + +# Comet 入口 + +`/comet` 只负责选择入口,不包含任何一种工作流的执行方法。 + +1. 先在当前项目尝试 PATH 中安装的 Comet CLI: + + ```text + comet workflow resolve . --json + ``` + +2. **只有**宿主明确报告 `command not found`、`executable not found` 或 `ENOENT`,能够证明 `comet` 不在 PATH 时,才从当前 `SKILL.md` 所在目录定位 ``,运行自带入口 runtime: + + ```text + node /scripts/comet-entry-runtime.mjs . --json + ``` + + CLI 已启动但返回非零、配置解析失败、输出不是 JSON 或字段无效,都不得使用 bundled runtime 重试;停止并原样说明错误,不要回退或猜测。 +3. 解析 JSON。只接受 `schema: comet.workflow-resolution.v1`,且 `skill` 必须是下列两个值之一。 +4. 只按返回的 `skill` 调用一个入口,并把用户原始请求完整交给它: + - `comet-native` → `/comet-native` + - `comet-classic` → `/comet-classic` + +不根据任务大小、文件数量、活跃 change 或模型判断改选另一套工作流。Native 与 Classic 的 change、状态和产物始终彼此独立。 diff --git a/.agents/skills/comet/scripts/comet-entry-runtime.mjs b/.agents/skills/comet/scripts/comet-entry-runtime.mjs new file mode 100644 index 0000000..1775481 --- /dev/null +++ b/.agents/skills/comet/scripts/comet-entry-runtime.mjs @@ -0,0 +1,7946 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path5) { + const ctrl = callVisitor(key, node, visitor, path5); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path5, ctrl); + return visit_(key, ctrl, visitor, path5); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path5 = Object.freeze(path5.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path5); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path5 = Object.freeze(path5.concat(node)); + const ck = visit_("key", node.key, visitor, path5); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path5); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path5) { + const ctrl = await callVisitor(key, node, visitor, path5); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path5, ctrl); + return visitAsync_(key, ctrl, visitor, path5); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path5 = Object.freeze(path5.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path5); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path5 = Object.freeze(path5.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path5); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path5); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path5) { + if (typeof visitor === "function") + return visitor(key, node, path5); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path5); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path5); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path5); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path5); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path5); + return void 0; + } + function replaceNode(key, path5, node) { + const parent = path5[path5.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path5, value) { + let v = value; + for (let i = path5.length - 1; i >= 0; --i) { + const k = path5[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path5) => path5 == null || typeof path5 === "object" && !!path5[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path5, value) { + if (isEmptyPath(path5)) + this.add(value); + else { + const [key, ...rest] = path5; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path5) { + const [key, ...rest] = path5; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path5, keepScalar) { + const [key, ...rest] = path5; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path5) { + const [key, ...rest] = path5; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path5, value) { + const [key, ...rest] = path5; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify2(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify2.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify2.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify2 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify2.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify3 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify3(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify2.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify2.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp.intTime, + timestamp.floatTime, + timestamp.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify2.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify2.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify2.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path5, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path5, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path5) { + if (Collection.isEmptyPath(path5)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path5) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path5, keepScalar) { + if (Collection.isEmptyPath(path5)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path5, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path5) { + if (Collection.isEmptyPath(path5)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path5) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path5, value) { + if (Collection.isEmptyPath(path5)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path5), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path5, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify2 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path5) => { + let item = cst; + for (const [field, index] of path5) { + const tok = item?.[field]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path5) => { + const parent = visit.itemAtPath(cst, path5.slice(0, -1)); + const field = path5[path5.length - 1][0]; + const coll = parent?.[field]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path5, item, visitor) { + let ctrl = visitor(item, path5); + if (typeof ctrl === "symbol") + return ctrl; + for (const field of ["key", "value"]) { + const token = item[field]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path5.concat([[field, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field === "key") + ctrl = ctrl(item, path5); + } + } + return typeof ctrl === "function" ? ctrl(item, path5) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs4 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs4, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs4); + } else { + Object.assign(it, { key: fs4, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs4 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs4, sep: [] }); + else if (it.sep) + this.stack.push(fs4); + else + Object.assign(it, { key: fs4, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument3(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument3(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify2(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument3; + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/comet-entry/entry-runtime.ts +import path4 from "path"; + +// domains/comet-native/native-config.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { promises as fs3 } from "fs"; +import path3 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml = __toESM(require_dist(), 1); + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// domains/comet-native/native-protected-file.ts +import { createHash } from "node:crypto"; +import { constants as fsConstants, promises as fs } from "node:fs"; +import path from "node:path"; +import { TextDecoder } from "node:util"; +function isInside(parent, target) { + const relative = path.relative(parent, target); + return relative === "" || !path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`); +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity(directory, label) { + const stat = await fs.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory, label) { + const lexicalRoot = path.resolve(root); + const lexicalDirectory = path.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path.relative(lexicalRoot, lexicalDirectory).split(path.sep).filter(Boolean)) { + await verifyDirectoryChain(chain, label); + cursor = path.join(cursor, segment); + const identity = await captureDirectoryIdentity(cursor, label); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain(chain, label); + return chain; +} +async function verifyDirectoryChain(chain, label) { + for (const identity of chain) { + const stat = await fs.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit(options.maxBytes); + const file = path.resolve(options.file); + const chain = await captureDirectoryChain(options.root, path.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity(path.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain(chain, options.label); + const before = await fs.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs.realpath(file); + if (!isInside(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + const handle = await fs.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs.lstat(file), + fs.realpath(file) + ]); + await verifyDirectoryChain(chain, options.label); + await verifyDirectoryChain(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity(beforeIdentity, opened) || !sameFileIdentity(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs.lstat(file), + fs.realpath(file) + ]); + await verifyDirectoryChain(chain, options.label); + await verifyDirectoryChain(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity(beforeIdentity, afterHandle) || !sameFileIdentity(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(snapshot.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot, text }; +} + +// domains/comet-native/native-paths.ts +import { promises as fs2 } from "fs"; +import path2 from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs2.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs2.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path2.resolve(startPath); + try { + if (!(await fs2.stat(cursor)).isDirectory()) cursor = path2.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path2.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path2.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path2.join(cursor, ".git"))) return cursor; + const parent = path2.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path2.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized = path2.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized === ".." || normalized.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized === "" ? "." : normalized; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot = record(value, "native.snapshot"); + rejectUnknown(snapshot, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path3.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs3.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} + +// domains/comet-entry/resolve-entry.ts +function configuredResolution(workflow) { + return { + workflow, + skill: workflow === "native" ? "comet-native" : "comet-classic", + source: "project-config" + }; +} +async function resolveCometEntry(startPath) { + const projectRoot = await discoverNativeProject(startPath); + const config = await readProjectConfig(projectRoot); + if (!config) { + return { + workflow: "classic", + skill: "comet-classic", + source: "legacy-fallback" + }; + } + return configuredResolution(config.default_workflow); +} + +// domains/comet-entry/workflow-resolution.ts +var COMET_WORKFLOW_RESOLUTION_SCHEMA = "comet.workflow-resolution.v1"; +async function resolveCometWorkflowResolution(startPath) { + return { + schema: COMET_WORKFLOW_RESOLUTION_SCHEMA, + ...await resolveCometEntry(startPath) + }; +} +function formatCometWorkflowResolution(resolution) { + return [ + `workflow: ${resolution.workflow}`, + `skill: ${resolution.skill}`, + `source: ${resolution.source}` + ].join("\n"); +} + +// domains/comet-entry/entry-runtime.ts +var USAGE = "Usage: comet-entry-runtime [path] [--json]"; +function parseEntryRuntimeArgs(args) { + let help = false; + let json = false; + let targetPath = null; + for (const arg of args) { + if (arg === "--help" || arg === "-h") { + help = true; + continue; + } + if (arg === "--json") { + json = true; + continue; + } + if (arg.startsWith("-")) { + throw new Error(`Unknown option: ${arg}`); + } + if (targetPath !== null) { + throw new Error(`Unexpected argument: ${arg}`); + } + targetPath = arg; + } + return { help, json, targetPath: path4.resolve(targetPath ?? ".") }; +} +async function runCometEntryRuntime(args, io = { + stdout: (value) => process.stdout.write(value), + stderr: (value) => process.stderr.write(value) +}) { + let parsed; + try { + parsed = parseEntryRuntimeArgs(args); + } catch (error) { + io.stderr(`${error instanceof Error ? error.message : String(error)} +${USAGE} +`); + return 64; + } + if (parsed.help) { + io.stdout(`${USAGE} +`); + return 0; + } + try { + const resolution = await resolveCometWorkflowResolution(parsed.targetPath); + io.stdout( + parsed.json ? `${JSON.stringify(resolution, null, 2)} +` : `${formatCometWorkflowResolution(resolution)} +` + ); + return 0; + } catch (error) { + io.stderr(`${error instanceof Error ? error.message : String(error)} +`); + return 65; + } +} + +// domains/comet-entry/entry-runtime-entry.ts +process.exitCode = await runCometEntryRuntime(process.argv.slice(2)); diff --git a/.agents/skills/comet/scripts/comet-hook-router.mjs b/.agents/skills/comet/scripts/comet-hook-router.mjs new file mode 100644 index 0000000..c1c83b9 --- /dev/null +++ b/.agents/skills/comet/scripts/comet-hook-router.mjs @@ -0,0 +1,10525 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __esm = (fn, res, err) => function __init() { + if (err) throw err[0]; + try { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; + } catch (e) { + throw err = [e], e; + } +}; +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path14) { + const ctrl = callVisitor(key, node, visitor, path14); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path14, ctrl); + return visit_(key, ctrl, visitor, path14); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path14 = Object.freeze(path14.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path14); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path14 = Object.freeze(path14.concat(node)); + const ck = visit_("key", node.key, visitor, path14); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path14); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path14) { + const ctrl = await callVisitor(key, node, visitor, path14); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path14, ctrl); + return visitAsync_(key, ctrl, visitor, path14); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path14 = Object.freeze(path14.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path14); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path14 = Object.freeze(path14.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path14); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path14); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path14) { + if (typeof visitor === "function") + return visitor(key, node, path14); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path14); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path14); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path14); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path14); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path14); + return void 0; + } + function replaceNode(key, path14, node) { + const parent = path14[path14.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path14, value) { + let v = value; + for (let i = path14.length - 1; i >= 0; --i) { + const k = path14[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path14) => path14 == null || typeof path14 === "object" && !!path14[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path14, value) { + if (isEmptyPath(path14)) + this.add(value); + else { + const [key, ...rest] = path14; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path14) { + const [key, ...rest] = path14; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path14, keepScalar) { + const [key, ...rest] = path14; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path14) { + const [key, ...rest] = path14; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path14, value) { + const [key, ...rest] = path14; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify3(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify3.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify3.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify3 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify3.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify4 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify4(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify3.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify3.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp.intTime, + timestamp.floatTime, + timestamp.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify3.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify3.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify3.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document2 = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path14, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path14, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path14) { + if (Collection.isEmptyPath(path14)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path14) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path14, keepScalar) { + if (Collection.isEmptyPath(path14)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path14, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path14) { + if (Collection.isEmptyPath(path14)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path14) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path14, value) { + if (Collection.isEmptyPath(path14)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path14), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path14, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document2 = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document2.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document2 = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document2.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify3 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path14) => { + let item = cst; + for (const [field2, index] of path14) { + const tok = item?.[field2]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path14) => { + const parent = visit.itemAtPath(cst, path14.slice(0, -1)); + const field2 = path14[path14.length - 1][0]; + const coll = parent?.[field2]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path14, item, visitor) { + let ctrl = visitor(item, path14); + if (typeof ctrl === "symbol") + return ctrl; + for (const field2 of ["key", "value"]) { + const token = item[field2]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path14.concat([[field2, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field2 === "key") + ctrl = ctrl(item, path14); + } + } + return typeof ctrl === "function" ? ctrl(item, path14) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs14 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs14, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs14); + } else { + Object.assign(it, { key: fs14, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs14 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs14, sep: [] }); + else if (it.sep) + this.stack.push(fs14); + else + Object.assign(it, { key: fs14, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document2 = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument6(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument6(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify3(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document2.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument6; + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document2 = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document2.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/engine/state.ts +var state_exports = {}; +__export(state_exports, { + RUN_STATE_FILE: () => RUN_STATE_FILE, + applyRunStateToDocument: () => applyRunStateToDocument, + readRunState: () => readRunState, + removeRunState: () => removeRunState, + runStateFromDocument: () => runStateFromDocument, + writeRunState: () => writeRunState +}); +import { randomUUID as randomUUID2 } from "crypto"; +import { promises as fs4 } from "fs"; +import path4 from "path"; +function requiredString(doc, key) { + const value = doc[key]; + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Invalid Run state: ${key} must be a non-empty string`); + } + return value; +} +function requiredRunReference(doc, key) { + const value = requiredString(doc, key); + if (path4.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Run state: ${key} must stay inside the change directory`); + } + return value; +} +function retries(doc) { + const raw = doc.run_retries ?? "{}"; + let value; + try { + value = typeof raw === "string" ? JSON.parse(raw) : raw; + } catch (error) { + throw new Error("Invalid Run state: run_retries must be a JSON object", { cause: error }); + } + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: run_retries must be a JSON object"); + } + for (const count of Object.values(value)) { + if (!Number.isInteger(count) || Number(count) < 0) { + throw new Error("Invalid Run state: retry counts must be non-negative integers"); + } + } + return value; +} +function runStateFromDocument(doc) { + if (!doc.run_id) return null; + const runId = requiredString(doc, "run_id"); + const skill = requiredString(doc, "skill"); + const skillVersion = requiredString(doc, "skill_version"); + const skillHash = requiredString(doc, "skill_hash"); + const pendingRef = requiredRunReference(doc, "pending_ref"); + const trajectoryRef = requiredRunReference(doc, "trajectory_ref"); + const contextRef = requiredRunReference(doc, "context_ref"); + const artifactsRef = requiredRunReference(doc, "artifacts_ref"); + const checkpointRef = requiredRunReference(doc, "checkpoint_ref"); + const iteration = Number(doc.iteration); + if (!Number.isInteger(iteration) || iteration < 0) { + throw new Error("Invalid Run state: iteration must be a non-negative integer"); + } + if (doc.orchestration !== "deterministic" && doc.orchestration !== "adaptive") { + throw new Error("Invalid Run state: orchestration must be deterministic or adaptive"); + } + if (doc.run_status !== "running" && doc.run_status !== "waiting" && doc.run_status !== "completed" && doc.run_status !== "failed") { + throw new Error("Invalid Run state: run_status is invalid"); + } + return { + runId, + skill, + skillVersion, + skillHash, + orchestration: doc.orchestration, + currentStep: field(doc, "current_step"), + iteration, + pending: field(doc, "pending"), + pendingRef, + trajectoryRef, + contextRef, + artifactsRef, + checkpointRef, + status: doc.run_status, + retries: retries(doc) + }; +} +function applyRunStateToDocument(doc, state) { + if (state) { + doc.run_id = state.runId; + } else { + delete doc.run_id; + } +} +function runStateToJson(state) { + return { + runId: state.runId, + skill: state.skill, + skillVersion: state.skillVersion, + skillHash: state.skillHash, + orchestration: state.orchestration, + currentStep: state.currentStep, + iteration: state.iteration, + pending: state.pending, + pendingRef: state.pendingRef, + trajectoryRef: state.trajectoryRef, + contextRef: state.contextRef, + artifactsRef: state.artifactsRef, + checkpointRef: state.checkpointRef, + status: state.status, + retries: state.retries + }; +} +function runStateFromJson(json) { + const doc = { + run_id: json.runId, + skill: json.skill, + skill_version: json.skillVersion, + skill_hash: json.skillHash, + orchestration: json.orchestration, + current_step: json.currentStep, + iteration: json.iteration, + pending: json.pending, + pending_ref: json.pendingRef, + trajectory_ref: json.trajectoryRef, + context_ref: json.contextRef, + artifacts_ref: json.artifactsRef, + checkpoint_ref: json.checkpointRef, + run_status: json.status, + run_retries: JSON.stringify(json.retries) + }; + return runStateFromDocument(doc); +} +async function readRunState(changeDir) { + const file = path4.join(changeDir, RUN_STATE_FILE); + let raw; + try { + raw = await fs4.readFile(file, "utf8"); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const json = JSON.parse(raw); + return runStateFromJson(json); +} +async function writeRunState(changeDir, state) { + await fs4.mkdir(path4.join(changeDir, ".comet"), { recursive: true }); + const file = path4.join(changeDir, RUN_STATE_FILE); + const temporary = path4.join(changeDir, ".comet", `run-state.${randomUUID2()}.tmp`); + await fs4.writeFile(temporary, JSON.stringify(runStateToJson(state), null, 2), "utf8"); + await fs4.rename(temporary, file); +} +async function removeRunState(changeDir) { + await fs4.rm(path4.join(changeDir, RUN_STATE_FILE), { force: true }); +} +var field, RUN_STATE_FILE; +var init_state = __esm({ + "domains/engine/state.ts"() { + "use strict"; + field = (doc, key) => { + const value = doc[key]; + return value === null || value === void 0 ? null : String(value); + }; + RUN_STATE_FILE = ".comet/run-state.json"; + } +}); + +// domains/comet-entry/hook-router-entry.ts +import path13 from "path"; +import { promises as fs13 } from "fs"; + +// domains/comet-native/native-paths.ts +import { promises as fs } from "fs"; +import path from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +function inside(parent, target) { + const relative = path.relative(parent, target); + return relative === "" || !path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`); +} +async function physicalPath(target) { + const missing = []; + let cursor = target; + while (!await isFileOrDirectory(cursor)) { + const parent = path.dirname(cursor); + if (parent === cursor) break; + missing.push(path.basename(cursor)); + cursor = parent; + } + const existing = await fs.realpath(cursor); + return path.resolve(existing, ...missing.reverse()); +} +async function isSymbolicLink(target) { + try { + return (await fs.lstat(target)).isSymbolicLink(); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path.resolve(startPath); + try { + if (!(await fs.stat(cursor)).isDirectory()) cursor = path.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path.join(cursor, ".git"))) return cursor; + const parent = path.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized2 = path.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized2 === ".." || normalized2.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized2 === "" ? "." : normalized2; +} +async function resolveArtifactRoot(projectRoot, value) { + const normalized2 = normalizeArtifactRootRef(value); + const lexical = path.resolve(projectRoot, ...normalized2.split("/")); + const physicalProject = await fs.realpath(projectRoot); + const physicalTarget = await physicalPath(lexical); + if (!inside(physicalProject, physicalTarget)) { + throw new Error("native.artifact_root resolves outside the project root"); + } + return lexical; +} +async function nativeProjectPaths(projectRoot, artifactRootRef) { + const normalized2 = normalizeArtifactRootRef(artifactRootRef); + const artifactRoot = await resolveArtifactRoot(projectRoot, normalized2); + const nativeRoot = path.join(artifactRoot, "comet"); + if (await isSymbolicLink(nativeRoot)) { + throw new Error("The configured Native comet root must not be a symbolic link"); + } + const [physicalArtifactRoot, physicalNativeRoot] = await Promise.all([ + physicalPath(artifactRoot), + physicalPath(nativeRoot) + ]); + if (!inside(physicalArtifactRoot, physicalNativeRoot)) { + throw new Error("The configured Native comet root resolves outside its artifact root"); + } + return { + projectRoot: path.resolve(projectRoot), + configFile: path.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")), + artifactRoot, + artifactRootRef: normalized2, + nativeRoot, + specsDir: path.join(nativeRoot, "specs"), + changesDir: path.join(nativeRoot, "changes"), + archiveDir: path.join(nativeRoot, "archive"), + runtimeDir: path.join(nativeRoot, "runtime"), + locksDir: path.join(nativeRoot, "runtime", "locks"), + transactionsDir: path.join(nativeRoot, "runtime", "transactions") + }; +} +function isInsidePath(parent, target) { + return inside(path.resolve(parent), path.resolve(target)); +} +async function resolveContainedNativePath(root, target) { + const lexicalRoot = path.resolve(root); + const lexicalTarget = path.resolve(target); + if (!inside(lexicalRoot, lexicalTarget)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + if (await isSymbolicLink(lexicalRoot)) { + throw new Error(`Native root must not be a symbolic link: ${root}`); + } + const [physicalRoot, physicalTarget] = await Promise.all([ + physicalPath(lexicalRoot), + physicalPath(lexicalTarget) + ]); + if (!inside(physicalRoot, physicalTarget)) { + throw new Error(`Path resolves outside the Native root: ${target}`); + } + return lexicalTarget; +} + +// domains/comet-entry/hook-adapter.ts +import { readFileSync } from "fs"; +var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "applypatch", + "create", + "createfile", + "deletefile", + "edit", + "editfile", + "patch", + "strreplaceeditor", + "write", + "writefile", + "writefiletool" +]); +var NON_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "glob", + "grep", + "listfiles", + "read", + "readfile", + "search", + "view" +]); +var SINGULAR_PATH_KEYS = ["file_path", "filePath", "path", "target_file", "targetFile"]; +var PLURAL_PATH_KEYS = ["file_paths", "filePaths", "paths", "files", "targets"]; +var NESTED_TARGET_KEYS = ["operations", "edits"]; +var PATCH_KEYS = ["patch", "diff", "patchText", "patch_text", "changes"]; +var COMET_HOOK_PLATFORM_IDS = /* @__PURE__ */ new Set([ + "claude", + "codex", + "windsurf", + "github-copilot", + "gemini", + "amazon-q", + "qwen", + "kiro", + "codebuddy", + "qoder" +]); +function isRecord(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function normalizedToolName(value) { + return value.toLowerCase().replace(/[^a-z0-9]+/gu, ""); +} +function readToolName(input) { + for (const key of ["tool_name", "toolName", "tool", "name"]) { + const value = input[key]; + if (typeof value === "string" && value.trim()) return value.trim(); + } + return null; +} +function parseJsonValue(value) { + if (typeof value !== "string") return value; + const source = value.trim(); + if (!source.startsWith("{") && !source.startsWith("[")) return value; + try { + return JSON.parse(source); + } catch { + return value; + } +} +function readToolArguments(input) { + for (const key of ["tool_input", "toolInput", "toolArgs", "tool_args", "arguments"]) { + if (input[key] !== void 0) return parseJsonValue(input[key]); + } + return input; +} +function patchTargets(source) { + const targets = []; + const patterns = [ + /^\*\*\* (?:Add|Update|Delete) File:\s+(.+?)\s*$/gmu, + /^\+\+\+\s+(?:b\/)?(.+?)\s*$/gmu + ]; + for (const pattern of patterns) { + for (const match of source.matchAll(pattern)) { + const target = match[1]?.trim(); + if (target && target !== "/dev/null") targets.push(target); + } + } + return targets; +} +function addTarget(targets, value) { + if (typeof value === "string") { + const target = value.trim(); + if (target) targets.push(target); + return; + } + if (Array.isArray(value)) { + for (const entry of value) addTarget(targets, entry); + return; + } + if (!isRecord(value)) return; + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, value[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, value[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, value[key]); +} +function collectTargets(input, args) { + const targets = []; + const records = [args, input].filter(isRecord); + for (const record3 of records) { + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, record3[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, record3[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, record3[key]); + for (const key of PATCH_KEYS) { + const value = record3[key]; + if (typeof value === "string") targets.push(...patchTargets(value)); + } + } + if (typeof args === "string") targets.push(...patchTargets(args)); + return [...new Set(targets)]; +} +function parseCometHookRequest(source, filePath) { + if (filePath?.trim()) { + return { intent: "write", targets: [filePath.trim()], toolName: null }; + } + if (!source.trim()) return { intent: "unknown", targets: [], toolName: null }; + let input; + try { + input = JSON.parse(source); + } catch { + return { intent: "unknown", targets: [], toolName: null }; + } + if (!isRecord(input)) return { intent: "unknown", targets: [], toolName: null }; + const toolName = readToolName(input); + const targets = collectTargets(input, readToolArguments(input)); + if (toolName && WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName + }; + } + if (toolName && NON_WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { intent: "non-write", targets: [], toolName }; + } + if (toolName) return { intent: "unknown", targets, toolName }; + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName: null + }; +} +function readCometHookRequest() { + const filePath = process.env.FILE_PATH; + if (filePath?.trim()) return parseCometHookRequest("", filePath); + if (process.stdin.isTTY) return parseCometHookRequest("", filePath); + try { + return parseCometHookRequest(readFileSync(0, "utf8"), filePath); + } catch { + return parseCometHookRequest("", filePath); + } +} +function renderCometHookDecision(platformId, decision) { + if (!COMET_HOOK_PLATFORM_IDS.has(platformId)) { + return { + exitCode: 64, + stdout: "", + stderr: `Unsupported Comet Hook platform: ${platformId} +` + }; + } + if (platformId === "github-copilot") { + return { + exitCode: 0, + stdout: decision.allowed ? "{}\n" : `${JSON.stringify({ + permissionDecision: "deny", + permissionDecisionReason: decision.reason + })} +`, + stderr: "" + }; + } + if (decision.allowed) return { exitCode: 0, stdout: "", stderr: "" }; + return { exitCode: 2, stdout: "", stderr: `${decision.reason} +` }; +} + +// domains/comet-classic/classic-hook-guard.ts +import { existsSync, promises as fs7, readFileSync as readFileSync2 } from "fs"; +import path7 from "path"; + +// domains/comet-classic/classic-branch-binding.ts +var import_yaml = __toESM(require_dist(), 1); +import { execFileSync } from "child_process"; +import { randomUUID } from "crypto"; +import { promises as fs2 } from "fs"; +import path2 from "path"; +function liveGitBranch(cwd) { + try { + const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"] + }).trim(); + return branch && branch !== "HEAD" ? branch : null; + } catch { + return null; + } +} +function isGitWorkTree(cwd) { + try { + return execFileSync("git", ["rev-parse", "--is-inside-work-tree"], { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"] + }).trim() === "true"; + } catch { + return false; + } +} +var BOUND_BRANCH_ISOLATIONS = ["current", "branch", "worktree"]; +function requiresBranchBinding(isolation) { + return BOUND_BRANCH_ISOLATIONS.includes(isolation); +} +function evaluateBranchBinding(input) { + if (!requiresBranchBinding(input.isolation)) return { status: "not-applicable" }; + if (input.boundBranch === null && input.currentBranch === null && input.gitWorkTree === false) { + return { status: "not-applicable" }; + } + if (input.boundBranch === null) { + return input.currentBranch === null ? { status: "unbound-detached" } : { status: "needs-heal", branch: input.currentBranch }; + } + if (input.currentBranch === input.boundBranch) return { status: "ok" }; + return { status: "drift", boundBranch: input.boundBranch, currentBranch: input.currentBranch }; +} +async function resolveBranchBinding(changeDir, options) { + const file = path2.join(changeDir, ".comet.yaml"); + const document = (0, import_yaml.parseDocument)(await fs2.readFile(file, "utf8"), { uniqueKeys: false }); + if (document.errors.length > 0) { + throw new Error(`Invalid .comet.yaml: ${document.errors[0].message}`); + } + const record3 = document.toJS() ?? {}; + const isolation = typeof record3.isolation === "string" ? record3.isolation : null; + const boundBranch = typeof record3.bound_branch === "string" && record3.bound_branch !== "" ? record3.bound_branch : null; + const bindingRequired = requiresBranchBinding(isolation); + const currentBranch = liveGitBranch(options.cwd); + const gitWorkTree = bindingRequired && boundBranch === null && currentBranch === null ? isGitWorkTree(options.cwd) : true; + const verdict = evaluateBranchBinding({ isolation, boundBranch, currentBranch, gitWorkTree }); + if (verdict.status === "needs-heal" && options.heal) { + await healBoundBranch(changeDir, verdict.branch); + return { status: "healed", branch: verdict.branch, bindingRequired, currentBranch }; + } + return { ...verdict, bindingRequired, currentBranch }; +} +async function healBoundBranch(changeDir, branch) { + const file = path2.join(changeDir, ".comet.yaml"); + const document = (0, import_yaml.parseDocument)(await fs2.readFile(file, "utf8"), { uniqueKeys: false }); + document.set("bound_branch", branch); + const temporary = `${file}.${randomUUID()}.tmp`; + try { + await fs2.writeFile(temporary, document.toString(), "utf8"); + await fs2.rename(temporary, file); + } catch (error) { + await fs2.rm(temporary, { force: true }); + throw error; + } +} +function branchLabel(currentBranch) { + return currentBranch ?? "detached HEAD"; +} +function driftStaleReason(change, boundBranch, currentBranch) { + return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'`; +} +function unboundDetachedMessage(change) { + return `change '${change}' uses a branch-bound workspace mode but has no bound branch and HEAD is detached; checkout a branch first before continuing.`; +} + +// domains/comet-classic/classic-current-change.ts +import { promises as fs6 } from "fs"; +import path6 from "path"; + +// domains/comet-entry/current-selection.ts +import path3 from "path"; + +// platform/fs/race-safe-read.ts +import { constants as fsConstants, promises as fs3 } from "fs"; + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function hasComparableFileObject(left, right) { + return hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev) && hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// platform/fs/race-safe-read.ts +var RaceSafeReadError = class extends Error { + reason; + constructor(reason, message, options) { + super(message, options); + this.name = "RaceSafeReadError"; + this.reason = reason; + } +}; +function birthtimeOf(stat) { + return "birthtimeNs" in stat && typeof stat.birthtimeNs === "bigint" ? stat.birthtimeNs : stat.birthtimeMs; +} +function ctimeOf(stat) { + return "ctimeNs" in stat && typeof stat.ctimeNs === "bigint" ? stat.ctimeNs : stat.ctimeMs; +} +function identityOf(stat) { + return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) }; +} +function sameStatIdentity(left, right) { + const leftObject = identityOf(left); + const rightObject = identityOf(right); + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && birthtimeOf(left) === birthtimeOf(right) && ctimeOf(left) === ctimeOf(right) && left.size === right.size; +} +async function readFileRaceSafe(file, maxBytes, options = {}) { + if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) { + throw new Error("race-safe read byte limit must be a positive integer"); + } + const label = options.label ?? "file"; + const bigint = options.bigint === true; + const before = await fs3.lstat(file, { bigint }); + if (!before.isFile() || before.isSymbolicLink()) { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`); + } + if (BigInt(before.size) > BigInt(maxBytes)) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + const beforeRealPath = await fs3.realpath(file); + await options.verify?.("pre-open", { realPath: beforeRealPath, identity: identityOf(before) }); + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + let handle; + try { + handle = await fs3.open(file, flags); + } catch (error) { + if (error.code === "ELOOP") { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`, { + cause: error + }); + } + throw error; + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat({ bigint }), + fs3.lstat(file, { bigint }), + fs3.realpath(file) + ]); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameStatIdentity(before, opened) || !sameStatIdentity(before, pathAfterOpen)) { + throw new RaceSafeReadError("changed", `${label} changed while opening`); + } + await options.verify?.("post-open", { + realPath: realPathAfterOpen, + identity: identityOf(opened) + }); + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + for (; ; ) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat({ bigint }), + fs3.lstat(file, { bigint }), + fs3.realpath(file) + ]); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameStatIdentity(before, afterHandle) || !sameStatIdentity(before, afterPath)) { + throw new RaceSafeReadError("changed", `${label} changed while reading`); + } + await options.verify?.("post-read", { + realPath: afterRealPath, + identity: identityOf(afterHandle) + }); + return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath }; + } finally { + await handle.close(); + } +} + +// domains/comet-entry/current-selection.ts +var COMET_CURRENT_SELECTION_SCHEMA = "comet.selection.v2"; +var COMET_CURRENT_SELECTION_MAX_BYTES = 16 * 1024; +function cometCurrentSelectionFile(projectRoot) { + return path3.join(projectRoot, ".comet", "current-change.json"); +} +function isRecord2(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function validBranch(value) { + return value === null || typeof value === "string"; +} +function parseSelection(source) { + let value; + try { + value = JSON.parse(source); + } catch (error) { + throw new Error( + `current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + if (!isRecord2(value)) { + throw new Error("current change selection must be a JSON object"); + } + if (value.version === 1) { + if (typeof value.change !== "string") { + throw new Error("legacy current change selection change must be a string"); + } + if (value.branch !== void 0 && !validBranch(value.branch)) { + throw new Error("legacy current change selection branch must be a string or null"); + } + return { + selection: { + schema: COMET_CURRENT_SELECTION_SCHEMA, + workflow: "classic", + change: value.change, + branch: value.branch ?? null + }, + legacy: true + }; + } + if (value.schema !== COMET_CURRENT_SELECTION_SCHEMA) { + throw new Error(`current change selection schema must be ${COMET_CURRENT_SELECTION_SCHEMA}`); + } + if (value.workflow !== "native" && value.workflow !== "classic") { + throw new Error("current change selection workflow must be native or classic"); + } + if (typeof value.change !== "string") { + throw new Error("current change selection change must be a string"); + } + if (!validBranch(value.branch)) { + throw new Error("current change selection branch must be a string or null"); + } + if (value.workflow === "native" && value.branch !== null) { + throw new Error("Native current change selection branch must be null"); + } + return { selection: value, legacy: false }; +} +async function readCometCurrentSelection(projectRoot) { + let source; + try { + const file = cometCurrentSelectionFile(projectRoot); + const { bytes } = await readFileRaceSafe(file, COMET_CURRENT_SELECTION_MAX_BYTES, { + label: "current change selection" + }); + source = bytes.toString("utf8"); + } catch (error) { + if (error.code === "ENOENT") return { status: "missing" }; + throw new Error( + `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + const parsed = parseSelection(source); + return { status: "selected", ...parsed }; +} + +// domains/comet-classic/classic-paths.ts +function openSpecChangeNameError(name) { + if (!name) return "Change name cannot be empty"; + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(name)) { + return `Invalid change name: '${name}' +Valid format: lowercase kebab-case (a-z, 0-9, single hyphens)`; + } + if (name.includes("..")) return "Change name cannot contain '..' (path traversal not allowed)"; + return null; +} +function assertOpenSpecChangeName(name) { + const error = openSpecChangeNameError(name); + if (error) throw new Error(error); +} + +// domains/comet-classic/classic-store.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { randomUUID as randomUUID3 } from "crypto"; +import { promises as fs5 } from "fs"; +import path5 from "path"; + +// domains/comet-classic/classic-state.ts +init_state(); +var CLASSIC_PROFILES = ["full", "hotfix", "tweak"]; +var CLASSIC_MIGRATION_VERSION = 1; +var PHASES = ["open", "design", "build", "verify", "archive"]; +var ARTIFACT_LANGUAGES = ["en", "zh-CN"]; +var CONTEXT_COMPRESSION = ["off", "beta"]; +var BUILD_MODES = ["subagent-driven-development", "executing-plans", "direct"]; +var BUILD_PAUSES = ["plan-ready"]; +var SUBAGENT_DISPATCH = ["confirmed"]; +var TDD_MODES = ["tdd", "direct"]; +var REVIEW_MODES = ["off", "standard", "thorough"]; +var ISOLATIONS = ["current", "branch", "worktree"]; +var VERIFY_MODES = ["light", "full"]; +var VERIFY_RESULTS = ["pending", "pass", "fail"]; +var BRANCH_STATUSES = ["pending", "handled"]; +var ARCHIVE_CONFIRMATIONS = ["pending", "confirmed"]; +var CLASSIC_WIRE_KEYS = [ + "workflow", + "language", + "phase", + "context_compression", + "build_mode", + "build_pause", + "subagent_dispatch", + "tdd_mode", + "review_mode", + "isolation", + "bound_branch", + "verify_mode", + "auto_transition", + "base_ref", + "design_doc", + "plan", + "verify_result", + "verify_failures", + "verification_report", + "branch_status", + "created_at", + "verified_at", + "archive_confirmation", + "archived", + "direct_override", + "handoff_context", + "handoff_hash", + "classic_profile", + "classic_migration" +]; +var RUN_WIRE_KEYS = ["run_id"]; +var KNOWN_KEYS = /* @__PURE__ */ new Set([...CLASSIC_WIRE_KEYS, ...RUN_WIRE_KEYS]); +var REQUIRED_CLASSIC_KEYS = [ + "workflow", + "phase", + "design_doc", + "plan", + "build_mode", + "isolation", + "verify_mode", + "verify_result", + "verified_at", + "archived" +]; +function has(doc, key) { + return Object.prototype.hasOwnProperty.call(doc, key); +} +function nullableString(doc, key) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") return null; + if (typeof value !== "string") { + throw new Error(`Invalid Classic state: ${key} must be a string or null`); + } + return value; +} +function enumValue(doc, key, values, nullable = true) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") { + if (nullable) return null; + throw new Error(`Invalid Classic state: ${key} is required`); + } + if (typeof value !== "string" || !values.includes(value)) { + throw new Error( + `Invalid Classic state: ${key} must be one of ${values.join(", ")}${nullable ? " or null" : ""}` + ); + } + return value; +} +function booleanValue(doc, key, nullable = true) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") { + if (nullable) return null; + throw new Error(`Invalid Classic state: ${key} is required`); + } + if (typeof value !== "boolean") { + throw new Error(`Invalid Classic state: ${key} must be true or false`); + } + return value; +} +function nonNegativeInteger(doc, key, fallback = 0) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") return fallback; + if (typeof value !== "number" || !Number.isInteger(value) || value < 0) { + throw new Error(`Invalid Classic state: ${key} must be a non-negative integer`); + } + return value; +} +function relativePath(doc, key) { + const value = nullableString(doc, key); + if (value === null) return null; + if (/^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Classic state: ${key} must be a relative repository path`); + } + return value; +} +function sha256(doc, key) { + const value = nullableString(doc, key); + if (value !== null && !/^[a-f0-9]{64}$/u.test(value)) { + throw new Error(`Invalid Classic state: ${key} must be a sha256 hex digest`); + } + return value; +} +function migrationVersion(doc) { + const value = doc.classic_migration; + if (value === null || value === void 0 || value === "") return null; + if (value !== CLASSIC_MIGRATION_VERSION) { + throw new Error( + `Invalid Classic state: classic_migration must be ${CLASSIC_MIGRATION_VERSION}` + ); + } + return value; +} +function classicStateFromDocument(doc) { + const hasClassicProjection = CLASSIC_WIRE_KEYS.some((key) => has(doc, key)); + if (!hasClassicProjection) return null; + for (const key of REQUIRED_CLASSIC_KEYS) { + if (!has(doc, key)) return null; + } + return { + workflow: enumValue(doc, "workflow", CLASSIC_PROFILES, false), + language: enumValue(doc, "language", ARTIFACT_LANGUAGES), + phase: enumValue(doc, "phase", PHASES, false), + contextCompression: enumValue(doc, "context_compression", CONTEXT_COMPRESSION), + buildMode: enumValue(doc, "build_mode", BUILD_MODES), + buildPause: enumValue(doc, "build_pause", BUILD_PAUSES), + subagentDispatch: enumValue(doc, "subagent_dispatch", SUBAGENT_DISPATCH), + tddMode: enumValue(doc, "tdd_mode", TDD_MODES), + reviewMode: enumValue(doc, "review_mode", REVIEW_MODES), + isolation: enumValue(doc, "isolation", ISOLATIONS), + boundBranch: nullableString(doc, "bound_branch"), + verifyMode: enumValue(doc, "verify_mode", VERIFY_MODES), + autoTransition: booleanValue(doc, "auto_transition"), + baseRef: nullableString(doc, "base_ref"), + designDoc: relativePath(doc, "design_doc"), + plan: relativePath(doc, "plan"), + verifyResult: enumValue(doc, "verify_result", VERIFY_RESULTS, false), + verifyFailures: nonNegativeInteger(doc, "verify_failures"), + verificationReport: relativePath(doc, "verification_report"), + branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES), + createdAt: nullableString(doc, "created_at"), + verifiedAt: nullableString(doc, "verified_at"), + archiveConfirmation: enumValue(doc, "archive_confirmation", ARCHIVE_CONFIRMATIONS), + archived: booleanValue(doc, "archived", false), + directOverride: booleanValue(doc, "direct_override"), + handoffContext: relativePath(doc, "handoff_context"), + handoffHash: sha256(doc, "handoff_hash"), + classicProfile: enumValue(doc, "classic_profile", CLASSIC_PROFILES), + classicMigration: migrationVersion(doc) + }; +} +function parseClassicStateDocument(doc, run) { + let resolvedRun = run ?? null; + if (resolvedRun === null && run === void 0) { + if (doc.run_id && doc.skill) { + try { + resolvedRun = runStateFromDocument(doc); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(message.replace(/^Invalid Run state:/u, "Invalid Classic state:"), { + cause: error + }); + } + } + } + return { + classic: classicStateFromDocument(doc), + run: resolvedRun, + unknownKeys: Object.keys(doc).filter((key) => !KNOWN_KEYS.has(key)) + }; +} +function readLegacyStateSummary(doc) { + const workflowRaw = doc["workflow"]; + const phaseRaw = doc["phase"]; + const archivedRaw = doc["archived"]; + const designDocRaw = doc["design_doc"]; + return { + workflow: typeof workflowRaw === "string" && CLASSIC_PROFILES.includes(workflowRaw) ? workflowRaw : null, + phase: typeof phaseRaw === "string" && PHASES.includes(phaseRaw) ? phaseRaw : null, + archived: archivedRaw === true, + designDoc: typeof designDocRaw === "string" && designDocRaw !== "" ? designDocRaw : null, + unknownKeys: Object.keys(doc).filter((key) => !KNOWN_KEYS.has(key)) + }; +} + +// domains/comet-classic/classic-store.ts +init_state(); +function documentRecord(document) { + const value = document.toJS(); + if (value === null) return {}; + if (typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Classic state document: root must be a mapping"); + } + return value; +} +function stripLegacyRunFields(document) { + const LEGACY_RUN_KEYS = [ + "skill", + "skill_version", + "skill_hash", + "orchestration", + "current_step", + "iteration", + "pending", + "pending_ref", + "trajectory_ref", + "context_ref", + "artifacts_ref", + "checkpoint_ref", + "run_status", + "run_retries" + ]; + for (const key of LEGACY_RUN_KEYS) document.delete(key); +} +function stripLegacyCommandFields(document) { + let changed = false; + for (const key of ["build_command", "verify_command"]) { + if (document.has(key)) { + document.delete(key); + changed = true; + } + } + return changed; +} +async function readDocument(file) { + let source; + try { + source = await fs5.readFile(file, "utf8"); + } catch (error) { + if (error.code !== "ENOENT") throw error; + return new import_yaml2.Document({}); + } + const document = (0, import_yaml2.parseDocument)(source); + if (document.errors.length > 0) { + throw new Error(`Invalid Classic state document: ${document.errors[0].message}`); + } + documentRecord(document); + return document; +} +async function readClassicState(changeDir, options = {}) { + const shouldMigrate = options.migrate !== false; + const file = path5.join(changeDir, ".comet.yaml"); + const document = await readDocument(file); + let doc = documentRecord(document); + let migrated = stripLegacyCommandFields(document); + if (migrated) doc = documentRecord(document); + let run = await readRunState(changeDir); + if (!run && doc.run_id && doc.skill) { + const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports)); + run = runStateFromDocument2(doc); + if (run && shouldMigrate) { + await writeRunState(changeDir, run); + stripLegacyRunFields(document); + migrated = true; + } + } + if (migrated && shouldMigrate) { + const temporary = path5.join(changeDir, `.comet.yaml.${randomUUID3()}.tmp`); + await fs5.writeFile(temporary, document.toString(), "utf8"); + await fs5.rename(temporary, file); + } + return parseClassicStateDocument(documentRecord(document), run); +} +async function readLegacyState(changeDir) { + const document = await readDocument(path5.join(changeDir, ".comet.yaml")); + return readLegacyStateSummary(documentRecord(document)); +} + +// domains/comet-classic/classic-current-change.ts +function changeDirectory(projectRoot, changeName) { + return path6.join(projectRoot, "openspec", "changes", changeName); +} +async function validateActiveChange(projectRoot, changeName) { + assertOpenSpecChangeName(changeName); + const changeDir = changeDirectory(projectRoot, changeName); + try { + await fs6.access(path6.join(changeDir, ".comet.yaml")); + } catch (error) { + if (error.code === "ENOENT") { + throw new Error( + `Cannot select current change '${changeName}': active change state not found`, + { + cause: error + } + ); + } + throw error; + } + const projection = await readClassicState(changeDir, { migrate: false }); + if (!projection.classic) { + throw new Error(`Cannot select current change '${changeName}': Classic state is incomplete`); + } + if (projection.classic.archived) { + throw new Error(`Cannot select current change '${changeName}': change is archived`); + } +} +async function resolveCurrentChange(projectRoot) { + let current; + try { + current = await readCometCurrentSelection(projectRoot); + } catch (error) { + return { + status: "stale", + reason: error instanceof Error ? error.message : String(error) + }; + } + if (current.status === "missing") return { status: "missing" }; + if (current.selection.workflow !== "classic") { + return { + status: "stale", + reason: `current change '${current.selection.change}' belongs to Native, not Classic` + }; + } + const selection = current.selection; + try { + await validateActiveChange(projectRoot, selection.change); + } catch (error) { + return { + status: "stale", + reason: error instanceof Error ? error.message : String(error) + }; + } + const outcome = await resolveBranchBinding(changeDirectory(projectRoot, selection.change), { + heal: false, + cwd: projectRoot + }); + if (outcome.status === "drift") { + return { + status: "stale", + reason: driftStaleReason(selection.change, outcome.boundBranch, outcome.currentBranch) + }; + } + if (outcome.status === "unbound-detached") { + return { status: "stale", reason: unboundDetachedMessage(selection.change) }; + } + if (outcome.status === "ok") return { status: "selected", selection }; + if (selection.branch !== null && outcome.currentBranch !== selection.branch) { + return { + status: "stale", + reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${outcome.currentBranch ?? "detached HEAD"}'` + }; + } + return { status: "selected", selection }; +} + +// domains/comet-classic/classic-hook-guard.ts +function result(exitCode, message) { + return { exitCode, stderr: message + "\n" }; +} +function allowed(message) { + return result(0, `[COMET-HOOK] allowed: ${message}`); +} +function normalized(value) { + return value.replaceAll("\\", "/").replace(/\/+/gu, "/"); +} +function comparisonKey(value) { + const normalizedValue = normalized(value); + return process.platform === "win32" ? normalizedValue.toLowerCase() : normalizedValue; +} +function relativeToProjectRoot(target, projectRoot) { + const relative = normalized(path7.relative(projectRoot, target)); + if (relative === "") return ""; + if (relative.startsWith("../") || relative === ".." || path7.isAbsolute(relative)) return null; + return relative; +} +async function physicalPathForPossiblyMissingTarget(target) { + const resolved = path7.resolve(target); + const root = path7.parse(resolved).root; + const missingSegments = []; + let cursor = resolved; + while (cursor && cursor !== root) { + try { + const physicalBase = await fs7.realpath(cursor); + return path7.join(physicalBase, ...missingSegments.reverse()); + } catch (error) { + const code = error.code; + if (code !== "ENOENT" && code !== "ENOTDIR") throw error; + missingSegments.push(path7.basename(cursor)); + cursor = path7.dirname(cursor); + } + } + try { + const physicalRoot = await fs7.realpath(root); + return path7.join(physicalRoot, ...missingSegments.reverse()); + } catch { + return null; + } +} +async function projectRelative(target, projectRoot) { + const rawCandidate = path7.isAbsolute(target) ? target : path7.resolve(process.cwd(), target); + let candidate = normalized(rawCandidate); + const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot); + if (rootRelative !== null) return rootRelative; + try { + const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate); + const physicalRoot = await fs7.realpath(projectRoot); + if (physicalCandidate) { + const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot); + if (physicalRootRelative !== null) return physicalRootRelative; + candidate = normalized(physicalCandidate); + } + } catch { + if (!path7.isAbsolute(target)) return normalized(target).replace(/^\.\//u, ""); + } + return candidate.replace(/^\.\//u, ""); +} +async function loadGoverningChange(changeDir) { + try { + const projection = await readClassicState(changeDir, { migrate: false }); + const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort(); + if (unknownKeys.length > 0) { + throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`); + } + if (!projection.classic) throw new Error("Classic state projection is unavailable"); + return { + changeDir, + phase: projection.classic.phase, + classic: projection.classic, + archived: projection.classic.archived + }; + } catch { + const legacy = await readLegacyState(changeDir); + if (!legacy.phase) return null; + return { + changeDir, + phase: legacy.phase, + classic: null, + archived: legacy.archived + }; + } +} +async function activeChanges(projectRoot) { + const changesDir = path7.join(projectRoot, "openspec", "changes"); + const governingChanges = []; + if (!existsSync(changesDir)) return governingChanges; + for (const entry of (await fs7.readdir(changesDir, { withFileTypes: true })).sort( + (left, right) => left.name.localeCompare(right.name) + )) { + if (!entry.isDirectory() || entry.name === "archive") continue; + const changeDir = path7.join(changesDir, entry.name); + if (!existsSync(path7.join(changeDir, ".comet.yaml"))) continue; + const governing = await loadGoverningChange(changeDir); + if (!governing || governing.archived) continue; + governingChanges.push(governing); + } + return governingChanges; +} +async function listActiveClassicHookChanges(projectRoot) { + return (await activeChanges(projectRoot)).map((change) => ({ + workflow: "classic", + name: governingChangeName(change), + phase: change.phase + })); +} +function isSuperpowersArtifactPath(relativePath2) { + return comparisonKey(relativePath2).startsWith("docs/superpowers/"); +} +var SUPERPOWERS_ARTIFACT_SLOTS = [ + { + prefix: "docs/superpowers/specs/", + field: "designDoc", + wireField: "design_doc", + phase: "design" + }, + { + prefix: "docs/superpowers/plans/", + field: "plan", + wireField: "plan", + phase: "build" + }, + { + prefix: "docs/superpowers/reports/", + field: "verificationReport", + wireField: "verification_report", + phase: "verify" + } +]; +function standardSuperpowersArtifactSlot(relativePath2) { + const key = comparisonKey(relativePath2); + const slot = SUPERPOWERS_ARTIFACT_SLOTS.find((candidate) => key.startsWith(candidate.prefix)); + if (!slot) return null; + const fileName = key.slice(slot.prefix.length); + if (!fileName || fileName.includes("/") || !fileName.endsWith(".md")) return null; + return slot; +} +function superpowersArtifactValue(governing, slot) { + return governing.classic?.[slot.field] ?? null; +} +function allowsFirstSuperpowersArtifactWrite(governing, slot) { + return governing.classic !== null && governing.phase === slot.phase && !superpowersArtifactValue(governing, slot); +} +function allowsSuperpowersArtifacts(governing) { + return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify"; +} +function governingChangeName(governing) { + return governing.changeDir ? path7.basename(governing.changeDir) : null; +} +var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([ + "design", + "plan", + "verify", + "verification", + "verification-report", + "report" +]); +function escapeRegex(value) { + return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); +} +function matchesRecordedSuperpowersArtifact(relativePath2, governing) { + const artifactPaths = [ + governing.classic?.designDoc, + governing.classic?.plan, + governing.classic?.verificationReport + ]; + return artifactPaths.some( + (artifactPath) => artifactPath && comparisonKey(artifactPath) === comparisonKey(relativePath2) + ); +} +function matchesSuperpowersArtifactName(relativePath2, changeName) { + const fileName = relativePath2.split("/").at(-1) ?? relativePath2; + const stem = fileName.replace(/\.[^.]+$/u, ""); + if (stem === changeName) return true; + const suffixes = [...SUPERPOWERS_ARTIFACT_SUFFIXES].map(escapeRegex).join("|"); + const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u"); + return pattern.test(stem); +} +async function superpowersArtifactGoverningChange(relativePath2, projectRoot) { + const active = await activeChanges(projectRoot); + const recorded = active.find( + (governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing) + ); + if (recorded) return { governing: recorded, match: "recorded" }; + const eligible = active.filter(allowsSuperpowersArtifacts); + const named = eligible.filter((governing) => { + const name = governingChangeName(governing); + return name !== null && matchesSuperpowersArtifactName(relativePath2, name); + }).sort( + (a, b) => (governingChangeName(b)?.length ?? 0) - (governingChangeName(a)?.length ?? 0) + )[0]; + if (named) return { governing: named, match: "named" }; + return null; +} +async function repoSourceGoverningChange(projectRoot, relativePath2, selectedChangeName) { + const active = await activeChanges(projectRoot); + if (active.length === 0) return null; + if (selectedChangeName) { + const selected = active.find( + (governing) => governingChangeName(governing) === selectedChangeName + ); + return selected ?? { + blockedResult: blockedStaleSelection( + relativePath2, + `selected change '${selectedChangeName}' is no longer active` + ) + }; + } + const current = await resolveCurrentChange(projectRoot); + if (current.status === "stale") { + return { blockedResult: blockedStaleSelection(relativePath2, current.reason) }; + } + if (current.status === "selected") { + const selected = active.find( + (governing) => governingChangeName(governing) === current.selection.change + ); + if (selected) return selected; + return { + blockedResult: blockedStaleSelection( + relativePath2, + `selected change '${current.selection.change}' is no longer active` + ) + }; + } + if (active.length === 1) { + const sole = active[0]; + if (sole.changeDir !== null) { + const outcome = await resolveBranchBinding(sole.changeDir, { + heal: false, + cwd: projectRoot + }); + const name = governingChangeName(sole) ?? "unknown"; + if (outcome.status === "drift") { + return { + blockedResult: blockedStaleSelection( + relativePath2, + driftStaleReason(name, outcome.boundBranch, outcome.currentBranch) + ) + }; + } + if (outcome.status === "unbound-detached") { + return { + blockedResult: blockedStaleSelection(relativePath2, unboundDetachedMessage(name)) + }; + } + } + return sole; + } + return { + blockedResult: blockedMultipleChanges( + relativePath2, + active.map((governing) => governingChangeName(governing)).filter(Boolean) + ) + }; +} +async function governingChange(relativePath2, projectRoot, selectedChangeName) { + const prefix = "openspec/changes/"; + if (relativePath2.startsWith(prefix)) { + const rest = relativePath2.slice(prefix.length); + const [name] = rest.split("/"); + if (name && name !== "archive") { + const changeDir = path7.join(projectRoot, "openspec", "changes", name); + const stateFile = path7.join(changeDir, ".comet.yaml"); + if (existsSync(stateFile)) { + const governing = await loadGoverningChange(changeDir); + if (governing) return governing; + return { changeDir, phase: "open", classic: null, archived: false }; + } + return { changeDir, phase: "open", classic: null, archived: false }; + } + } + if (isSuperpowersArtifactPath(relativePath2)) { + const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot); + if (superpowers?.match === "recorded") { + return { ...superpowers.governing, superpowersArtifact: "matched" }; + } + const slot = standardSuperpowersArtifactSlot(relativePath2); + if (superpowers) { + return slot ? { + ...superpowers.governing, + superpowersArtifact: allowsFirstSuperpowersArtifactWrite(superpowers.governing, slot) ? "matched" : "unmatched", + superpowersSlot: slot + } : { ...superpowers.governing, superpowersArtifact: "matched" }; + } + if (slot) { + const candidate = await repoSourceGoverningChange( + projectRoot, + relativePath2, + selectedChangeName + ); + if (!candidate || "blockedResult" in candidate) return candidate; + return { + ...candidate, + superpowersArtifact: allowsFirstSuperpowersArtifactWrite(candidate, slot) ? "matched" : "unmatched", + superpowersSlot: slot + }; + } + const active = await activeChanges(projectRoot); + const fallback = selectedChangeName ? active.find((candidate) => governingChangeName(candidate) === selectedChangeName) ?? null : active[0] ?? null; + return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null; + } + return repoSourceGoverningChange(projectRoot, relativePath2, selectedChangeName); +} +function isRootMarkdown(relativePath2) { + return !relativePath2.includes("/") && relativePath2.endsWith(".md"); +} +function isCometConfig(relativePath2) { + return relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/"); +} +function isSuperpowersWorkspace(relativePath2) { + return relativePath2 === ".superpowers" || relativePath2.startsWith(".superpowers/"); +} +function openSpecAllowed(relativePath2, phase) { + if (!relativePath2.startsWith("openspec/")) return null; + const stateFile = relativePath2.endsWith("/.comet.yaml") || relativePath2.endsWith("/.openspec.yaml"); + const proposal = relativePath2.endsWith("/proposal.md") || relativePath2.endsWith("/design.md") || relativePath2.endsWith("/tasks.md"); + const handoff = relativePath2.includes("/.comet/"); + const specs = relativePath2.includes("/specs/"); + if (phase === "open" && (proposal || stateFile || handoff || specs)) { + return `${relativePath2} (phase: open, openspec artifacts)`; + } + if (phase === "design" && (proposal || stateFile || handoff || specs)) { + return `${relativePath2} (phase: design, handoff/spec)`; + } + if (phase === "build" && (relativePath2.endsWith("/tasks.md") || stateFile || specs)) { + return `${relativePath2} (phase: build, spec/tasks)`; + } + if (phase === "verify" && (relativePath2.endsWith("/tasks.md") || stateFile)) { + return `${relativePath2} (phase: verify, tasks/state)`; + } + if (phase === "archive" && stateFile) { + return `${relativePath2} (phase: archive, state)`; + } + return null; +} +function blocked(relativePath2, phase) { + const guidance = phase === "open" ? [ + " BLOCKED: source writes are not allowed during open", + " This phase does not allow source writes", + " ALLOWED: create proposal/design/tasks artifacts and run guard", + " NEXT: finish clarification and artifacts, then run guard --apply" + ] : phase === "design" ? [ + " BLOCKED: source writes are not allowed during design", + " This phase does not allow source writes", + " ALLOWED: run brainstorming, create the Design Doc, and run guard", + " NEXT: finish the Design Doc, then run comet guard design --apply to enter build" + ] : [ + " BLOCKED: source writes are not allowed during archive", + " This phase does not allow source writes", + " ALLOWED: confirm archive state and run the archive script" + ]; + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + ` Current phase: ${phase}`, + ` Target file: ${relativePath2}`, + "", + ...guidance, + "" + ].join("\n") + ); +} +function blockedMissingDesignDoc(relativePath2) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " Current phase: build (workflow: full), but design_doc is empty", + ` Target file: ${relativePath2}`, + "", + " BLOCKED: full workflow source writes require a recorded Design Doc", + " This phase does not allow source writes until design_doc is recorded", + " NEXT: return to design, create/link the Design Doc, then run guard again", + "" + ].join("\n") + ); +} +function blockedUnmatchedSuperpowersArtifact(relativePath2, governing) { + const slot = governing.superpowersSlot; + const recorded = slot ? superpowersArtifactValue(governing, slot) : null; + const details = slot ? governing.phase !== slot.phase ? [ + ` BLOCKED: ${slot.wireField} cannot be first-written in phase ${governing.phase}`, + ` Expected phase: ${slot.phase}`, + " NEXT: resume the matching Comet phase or use an already recorded artifact path" + ] : recorded ? [ + ` BLOCKED: ${slot.wireField} is already recorded for this change`, + ` Recorded path: ${recorded}`, + " NEXT: write the recorded artifact or explicitly correct the state path" + ] : [ + " BLOCKED: standard Superpowers artifact state is incomplete", + " NEXT: validate the active change state, then retry the matching phase" + ] : [ + " BLOCKED: unmatched Superpowers artifact", + " This docs/superpowers/ path does not match any active change artifact", + " NEXT: use a recorded artifact path or a standard phase artifact directory" + ]; + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + ` Current phase: ${governing.phase}`, + ` Target file: ${relativePath2}`, + "", + ...details, + "" + ].join("\n") + ); +} +function blockedMultipleChanges(relativePath2, changeNames) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " BLOCKED: multiple active changes require a current change", + ` Target file: ${relativePath2}`, + ` Active changes: ${changeNames.join(", ")}`, + "", + " NEXT: run comet state select , then retry the source write", + "" + ].join("\n") + ); +} +function blockedStaleSelection(relativePath2, reason) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " BLOCKED: current change selection is stale or invalid", + ` Target file: ${relativePath2}`, + ` Reason: ${reason}`, + "", + " NEXT: run comet state select , then retry the source write", + "" + ].join("\n") + ); +} +async function inspectClassicHookTarget(projectRoot, target, selectedChangeName) { + const relativePath2 = await projectRelative(target, projectRoot); + if (isCometConfig(relativePath2)) { + return allowed(`${relativePath2} (whitelist: comet config)`); + } + if (relativePath2.startsWith(".claude/")) { + return allowed(`${relativePath2} (whitelist: claude config)`); + } + if (isSuperpowersWorkspace(relativePath2)) { + return allowed(`${relativePath2} (whitelist: superpowers workspace)`); + } + if (relativePath2 === "CLAUDE.md" || relativePath2 === "CHANGELOG.md" || relativePath2 === "README.md" || isRootMarkdown(relativePath2)) { + return allowed(`${relativePath2} (whitelist: root markdown)`); + } + let governing; + try { + governing = await governingChange(relativePath2, projectRoot, selectedChangeName); + } catch (error) { + return result( + 2, + `[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}` + ); + } + if (!governing) return allowed("no active comet change"); + if ("blockedResult" in governing) return governing.blockedResult; + if (governing.archived) return allowed(`${relativePath2} (own change archived)`); + const phase = governing.phase; + const openSpec = openSpecAllowed(relativePath2, phase); + if (openSpec) return allowed(openSpec); + if (isSuperpowersArtifactPath(relativePath2)) { + if (governing.superpowersArtifact === "matched" && allowsSuperpowersArtifacts(governing)) { + return allowed(`${relativePath2} (phase: ${phase}, superpowers)`); + } + if (governing.superpowersArtifact === "unmatched") { + return blockedUnmatchedSuperpowersArtifact(relativePath2, governing); + } + } + if (phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc) { + return blockedMissingDesignDoc(relativePath2); + } + if (phase === "build" || phase === "verify") { + return allowed(`${relativePath2} (phase: ${phase})`); + } + return blocked(relativePath2, phase); +} +async function inspectClassicHookGuard(projectRoot, changeName, request) { + const active = await activeChanges(projectRoot); + const selected = active.find((change) => governingChangeName(change) === changeName); + if (!selected) { + return { + allowed: false, + reason: `Selected Classic change ${changeName} is missing or archived; resume /comet-classic before retrying`, + workflow: "classic", + change: changeName + }; + } + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + if (selected.phase === "verify" || selected.phase === "build" && !(selected.classic?.workflow === "full" && !selected.classic.designDoc)) { + return { + allowed: true, + reason: `Classic change is in ${selected.phase}`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + return { + allowed: false, + reason: `Hook write target could not be determined while Classic change ${changeName} is in ${selected.phase}; resume /comet-classic before retrying`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + for (const target of request.targets) { + const inspected = await inspectClassicHookTarget(projectRoot, target, changeName); + if (inspected.exitCode !== 0) { + return { + allowed: false, + reason: inspected.stderr?.trim() || "Classic phase guard blocked the write", + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + } + return { + allowed: true, + reason: `Classic write allowed in ${selected.phase}`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; +} + +// domains/comet-native/native-hook-guard.ts +import { promises as fs12 } from "fs"; +import path12 from "path"; + +// domains/comet-native/native-change.ts +var import_yaml5 = __toESM(require_dist(), 1); +import { promises as fs11 } from "fs"; +import path11 from "path"; + +// domains/comet-native/native-bounded-file.ts +import { createHash } from "node:crypto"; +import { promises as fs8 } from "node:fs"; +import path8 from "node:path"; +import { TextDecoder } from "node:util"; + +// domains/comet-native/native-sensitive-paths.ts +var NATIVE_EXCLUDED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([ + ".cache", + ".git", + ".gradle", + ".gnupg", + ".mypy_cache", + ".next", + ".npm", + ".pnpm-store", + ".pytest_cache", + ".ssh", + ".turbo", + ".venv", + ".yarn", + "__pycache__", + "node_modules", + "venv" +]); +var NATIVE_SENSITIVE_FILE_NAMES = /* @__PURE__ */ new Set([ + ".git-credentials", + ".netrc", + ".npmrc", + ".pypirc", + "auth.json", + "credentials.json" +]); +function isNativeEnvFileName(name) { + return name.toLowerCase().startsWith(".env"); +} +function nativeSensitiveRelativePathReason(relativeRef) { + const segments = relativeRef.replaceAll("\\", "/").split("/").filter(Boolean); + const lower = segments.map((segment) => segment.toLowerCase()); + if (lower.some((segment) => isNativeEnvFileName(segment))) return "environment-file"; + if (lower.some((segment) => NATIVE_SENSITIVE_FILE_NAMES.has(segment))) { + return "credential-config"; + } + if (lower.includes(".git")) return "git-metadata"; + if (lower.some((segment) => NATIVE_EXCLUDED_DIRECTORY_NAMES.has(segment))) { + return "dependency-or-cache"; + } + if (lower.join("/") === ".comet/config.yaml") { + return "comet-config"; + } + if (lower.join("/") === ".comet/current-change.json") { + return "comet-selection"; + } + return null; +} + +// domains/comet-native/native-bounded-file.ts +var DEFAULT_NATIVE_ARTIFACT_MAX_BYTES = 1024 * 1024; +function isInside(parent, target) { + const relative = path8.relative(parent, target); + return relative === "" || !path8.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path8.sep}`); +} +function portableArtifactRef(value) { + const normalized2 = path8.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path8.posix.isAbsolute(normalized2) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized2 !== value || normalized2 === "." || value.endsWith("/")) { + throw new Error(`Native artifact ref must be normalized and relative: ${value}`); + } + const lower = value.toLowerCase(); + const sensitiveReason = nativeSensitiveRelativePathReason(value); + if (sensitiveReason || lower === "runtime" || lower.startsWith("runtime/")) { + throw new Error( + `Native artifact ref is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${value}` + ); + } + return value; +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native artifact byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function directoryIdentity(directory) { + const stat = await fs8.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native artifact parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs8.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory) { + const lexicalRoot = path8.resolve(root); + const lexicalDirectory = path8.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error("Native artifact path is outside its root"); + } + const chain = [await directoryIdentity(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path8.relative(lexicalRoot, lexicalDirectory).split(path8.sep).filter(Boolean)) { + cursor = path8.join(cursor, segment); + const identity = await directoryIdentity(cursor); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`Native artifact parent resolves outside its root: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain(chain) { + for (const identity of chain) { + const stat = await fs8.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs8.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native artifact parent changed while reading: ${identity.path}`); + } + } +} +async function readNativeBoundedTextFile(options) { + const ref = portableArtifactRef(options.ref); + const maxBytes = positiveLimit(options.maxBytes ?? DEFAULT_NATIVE_ARTIFACT_MAX_BYTES); + const file = path8.resolve(options.root, ...ref.split("/")); + const chain = await captureDirectoryChain(options.root, path8.dirname(file)); + await options.hooks?.afterParentChainCaptured?.(); + const before = await fs8.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native artifact must be a regular file: ${ref}`); + } + if (before.size > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + const realPath = await fs8.realpath(file); + if (!isInside(chain[0].realPath, realPath)) { + throw new Error(`Native artifact resolves outside its root: ${ref}`); + } + const handle = await fs8.open(file, "r"); + try { + const [opened, afterOpenPath, afterOpenRealPath] = await Promise.all([ + handle.stat(), + fs8.lstat(file), + fs8.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!opened.isFile() || !afterOpenPath.isFile() || afterOpenPath.isSymbolicLink() || afterOpenRealPath !== realPath || !sameFileIdentity(before, opened) || !sameFileIdentity(opened, afterOpenPath)) { + throw new Error(`Native artifact changed while opening: ${ref}`); + } + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (result2.bytesRead === 0) break; + total += result2.bytesRead; + if (total > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + chunks.push(Buffer.from(buffer.subarray(0, result2.bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs8.lstat(file), + fs8.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity(opened, afterHandle) || !sameFileIdentity(opened, afterPath)) { + throw new Error(`Native artifact changed while reading: ${ref}`); + } + const bytes = Buffer.concat(chunks, total); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`Native artifact is not valid UTF-8: ${ref}`, { cause: error }); + } + return { + ref, + size: total, + hash: createHash("sha256").update(bytes).digest("hex"), + text + }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-config.ts +var import_yaml4 = __toESM(require_dist(), 1); +import { promises as fs10 } from "fs"; +import path10 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml3 = __toESM(require_dist(), 1); + +// domains/comet-native/native-protected-file.ts +import { createHash as createHash2 } from "node:crypto"; +import { constants as fsConstants2, promises as fs9 } from "node:fs"; +import path9 from "node:path"; +import { TextDecoder as TextDecoder2 } from "node:util"; +function isInside2(parent, target) { + const relative = path9.relative(parent, target); + return relative === "" || !path9.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path9.sep}`); +} +function positiveLimit2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity(directory, label) { + const stat = await fs9.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs9.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain2(root, directory, label) { + const lexicalRoot = path9.resolve(root); + const lexicalDirectory = path9.resolve(directory); + if (!isInside2(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path9.relative(lexicalRoot, lexicalDirectory).split(path9.sep).filter(Boolean)) { + await verifyDirectoryChain2(chain, label); + cursor = path9.join(cursor, segment); + const identity = await captureDirectoryIdentity(cursor, label); + if (!isInside2(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain2(chain, label); + return chain; +} +async function verifyDirectoryChain2(chain, label) { + for (const identity of chain) { + const stat = await fs9.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity2(identity, stat) || await fs9.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit2(options.maxBytes); + const file = path9.resolve(options.file); + const chain = await captureDirectoryChain2(options.root, path9.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity(path9.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain2(chain, options.label); + const before = await fs9.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs9.realpath(file); + if (!isInside2(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside2(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants2.O_RDONLY : fsConstants2.O_RDONLY | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK; + const handle = await fs9.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs9.lstat(file), + fs9.realpath(file) + ]); + await verifyDirectoryChain2(chain, options.label); + await verifyDirectoryChain2(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity2(beforeIdentity, opened) || !sameFileIdentity2(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs9.lstat(file), + fs9.realpath(file) + ]); + await verifyDirectoryChain2(chain, options.label); + await verifyDirectoryChain2(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity2(beforeIdentity, afterHandle) || !sameFileIdentity2(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash2("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder2("utf-8", { fatal: true }).decode(snapshot.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot, text }; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot = record(value, "native.snapshot"); + rejectUnknown(snapshot, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path10.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs10.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml4.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} + +// domains/comet-native/native-lock.ts +import { AsyncLocalStorage } from "async_hooks"; +var NATIVE_LOCK_MAX_BYTES = 16 * 1024; +var nativeLockCoordinator = new AsyncLocalStorage(); + +// domains/comet-native/native-transaction.ts +import { TextDecoder as TextDecoder3 } from "util"; +var NATIVE_TRANSACTION_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_TRANSACTION_EVENTS_MAX_BYTES = 1024 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_BYTES = 16 * 1024; +var NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES = 64 * 1024 * 1024; +var UTF8_DECODER = new TextDecoder3("utf-8", { fatal: true }); + +// domains/comet-native/native-snapshot.ts +var DEFAULT_NATIVE_SNAPSHOT_LIMITS = { + maxFiles: 1e4, + maxFileBytes: 5 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxManifestBytes: 1024 * 1024 +}; +var NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES = 8 * 1024 * 1024; +var GIT_LIST_STDERR_LIMIT = 64 * 1024; +var GIT_TEXT_STDOUT_LIMIT = 64 * 1024; +var DEFAULT_NATIVE_GIT_SELECTION_LIMITS = { + maxRecords: 2e4, + maxBytes: 8 * 1024 * 1024, + maxRecordBytes: 64 * 1024 +}; +var DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS = { + maxNodes: 2e4, + maxBytes: 8 * 1024 * 1024, + maxPathBytes: 64 * 1024 +}; +var PHYSICAL_SELECTION_SUM_MASK = (1n << 256n) - 1n; + +// domains/engine/storage-run.ts +init_state(); + +// domains/comet-native/native-run-store.ts +var NATIVE_RUN_IO_LIMITS = { + runStateBytes: 256 * 1024, + trajectoryBytes: 8 * 1024 * 1024, + trajectoryEvents: 4096, + trajectoryEventBytes: 256 * 1024, + checkpointBytes: 256 * 1024, + pendingActionBytes: 256 * 1024, + contextBytes: 1024 * 1024, + artifactsBytes: 1024 * 1024 +}; + +// domains/comet-native/native-workspace.ts +var MAX_WORKSPACE_IDENTITY_BYTES = 16 * 1024; + +// domains/comet-native/native-types.ts +var NATIVE_RUNTIME_PROTOCOL_VERSION = 3; +var NATIVE_CHANGE_SCHEMA = "comet.native.v3"; +var NATIVE_V2_CHANGE_SCHEMA = "comet.native.v2"; +var NATIVE_LEGACY_CHANGE_SCHEMA = "comet.native.v1"; + +// domains/comet-native/native-change.ts +var CHANGE_KEYS = [ + "schema", + "name", + "language", + "phase", + "brief", + "approval", + "spec_changes", + "verification_result", + "verification_report", + "archived", + "created_at", + "run_id" +]; +var LEGACY_CHANGE_KEYS = new Set(CHANGE_KEYS); +var V2_CHANGE_KEYS = /* @__PURE__ */ new Set([...CHANGE_KEYS, "minimum_runtime_version", "revision"]); +var CURRENT_CHANGE_KEYS = /* @__PURE__ */ new Set([ + ...V2_CHANGE_KEYS, + "approved_contract_hash", + "implementation_scope", + "verification_evidence", + "partial_allowance" +]); +var SPEC_CHANGE_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "source", "base_hash"]); +var PHASES2 = /* @__PURE__ */ new Set(["shape", "build", "verify", "archive"]); +var APPROVALS = /* @__PURE__ */ new Set(["implicit", "confirmed"]); +var VERIFY_RESULTS2 = /* @__PURE__ */ new Set(["pending", "pass", "fail"]); +var NATIVE_CHANGE_STATE_FILE = "comet-state.yaml"; +var HASH_PATTERN = /^[a-f0-9]{64}$/u; +var NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CONTENT_ADDRESSED_REF_PATTERN = /^runtime\/evidence\/(scopes|allowances|verifications)\/([a-f0-9]{64})\.json$/u; +var NativeSchemaMigrationRequiredError = class extends Error { + constructor(change, schema) { + super( + `Native change ${change} uses ${schema}; run comet native doctor ${change} --repair before mutating it` + ); + this.change = change; + this.schema = schema; + this.name = "NativeSchemaMigrationRequiredError"; + } + change; + schema; + code = "native-schema-migration-required"; +}; +var NativeRuntimeCompatibilityError = class extends Error { + constructor(schema, minimumRuntimeVersion) { + super( + schema !== NATIVE_CHANGE_SCHEMA || minimumRuntimeVersion === null ? `Unsupported Native change schema ${schema} for runtime protocol ${NATIVE_RUNTIME_PROTOCOL_VERSION}` : `Native change ${schema} requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + this.schema = schema; + this.minimumRuntimeVersion = minimumRuntimeVersion; + this.name = "NativeRuntimeCompatibilityError"; + } + schema; + minimumRuntimeVersion; + code = "native-runtime-incompatible"; +}; +var NATIVE_BRIEF_TEMPLATE = [ + "# Outcome", + "", + "# Scope", + "", + "# Non-goals", + "", + "# Acceptance examples", + "", + "# Constraints and invariants", + "", + "# Decisions", + "", + "# Open questions", + "", + "# Verification expectations", + "" +].join("\n"); +function record2(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown2(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function assertNativeName(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native change name: ${value}`); +} +function assertCapabilityId(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native capability id: ${value}`); +} +function assertRelativeRef(value, label) { + if (value.length === 0 || path11.isAbsolute(value) || /^(?:[A-Za-z]:|~|[\\/])/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native change`); + } +} +function parseSpecChange(value, index) { + const item = record2(value, `spec_changes[${index}]`); + rejectUnknown2(item, SPEC_CHANGE_KEYS, `spec_changes[${index}]`); + if (typeof item.capability !== "string") throw new Error("spec change capability is required"); + assertCapabilityId(item.capability); + if (item.operation !== "create" && item.operation !== "replace" && item.operation !== "remove") { + throw new Error(`Invalid spec operation for ${item.capability}`); + } + const source = item.source; + const baseHash = item.base_hash; + if (source !== void 0 && typeof source !== "string") { + throw new Error(`Spec source for ${item.capability} must be a string`); + } + if (typeof source === "string") assertRelativeRef(source, `Spec source for ${item.capability}`); + if (item.operation === "create") { + if (!source) throw new Error(`Create spec ${item.capability} requires source`); + if (baseHash !== null) + throw new Error(`Create spec ${item.capability} requires null base_hash`); + } else if (item.operation === "replace") { + if (!source) throw new Error(`Replace spec ${item.capability} requires source`); + if (typeof baseHash !== "string" || !HASH_PATTERN.test(baseHash)) { + throw new Error(`Replace spec ${item.capability} requires a SHA-256 base_hash`); + } + } else { + if (source !== void 0) throw new Error(`Remove spec ${item.capability} forbids source`); + if (typeof baseHash !== "string" || !HASH_PATTERN.test(baseHash)) { + throw new Error(`Remove spec ${item.capability} requires a SHA-256 base_hash`); + } + } + return { + capability: item.capability, + operation: item.operation, + ...typeof source === "string" ? { source } : {}, + base_hash: baseHash + }; +} +function validDate(value) { + if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) return false; + return (/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`)).toISOString().slice(0, 10) === value; +} +function parseChangeFields(root, knownKeys) { + rejectUnknown2(root, knownKeys, NATIVE_CHANGE_STATE_FILE); + if (typeof root.name !== "string") throw new Error("Native change name is required"); + assertNativeName(root.name); + if (root.language !== "en" && root.language !== "zh-CN") { + throw new Error("Native change language must be en or zh-CN"); + } + if (typeof root.phase !== "string" || !PHASES2.has(root.phase)) { + throw new Error("Native change phase is invalid"); + } + if (root.brief !== "brief.md") throw new Error("Native change brief must be brief.md"); + if (root.approval !== null && !APPROVALS.has(root.approval)) { + throw new Error("Native change approval is invalid"); + } + if (!Array.isArray(root.spec_changes)) throw new Error("Native spec_changes must be an array"); + const specChanges = root.spec_changes.map(parseSpecChange); + const duplicates = specChanges.map((change) => change.capability).filter((capability, index, all) => all.indexOf(capability) !== index); + if (duplicates.length > 0) { + throw new Error( + `Duplicate Native capability operation: ${[...new Set(duplicates)].join(", ")}` + ); + } + if (typeof root.verification_result !== "string" || !VERIFY_RESULTS2.has(root.verification_result)) { + throw new Error("Native verification_result is invalid"); + } + if (root.verification_report !== null && typeof root.verification_report !== "string") { + throw new Error("Native verification_report must be a string or null"); + } + if (typeof root.verification_report === "string") { + assertRelativeRef(root.verification_report, "Native verification_report"); + } + if (typeof root.archived !== "boolean") throw new Error("Native archived must be boolean"); + if (typeof root.created_at !== "string" || !validDate(root.created_at)) { + throw new Error("Native created_at must be a valid YYYY-MM-DD date"); + } + if (root.run_id !== null && (typeof root.run_id !== "string" || root.run_id.length === 0)) { + throw new Error("Native run_id must be a non-empty string or null"); + } + return { + name: root.name, + language: root.language, + phase: root.phase, + brief: "brief.md", + approval: root.approval, + spec_changes: specChanges, + verification_result: root.verification_result, + verification_report: root.verification_report, + archived: root.archived, + created_at: root.created_at, + run_id: root.run_id + }; +} +function parseLegacyNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_LEGACY_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_LEGACY_CHANGE_SCHEMA}`); + } + return { + schema: NATIVE_LEGACY_CHANGE_SCHEMA, + ...parseChangeFields(root, LEGACY_CHANGE_KEYS) + }; +} +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function contentAddressedRef(value, label, kind) { + if (value === null) return null; + const match = typeof value === "string" ? CONTENT_ADDRESSED_REF_PATTERN.exec(value) : null; + if (!match || match[1] !== kind) { + throw new Error( + `${label} must be null or runtime/evidence/${kind}/.json relative to the Native change` + ); + } + return value; +} +function approvedContractHash(value) { + if (value === void 0 || value === null) return null; + if (typeof value !== "string" || !HASH_PATTERN.test(value)) { + throw new Error("Native approved_contract_hash must be null or a SHA-256 hash"); + } + return value; +} +function parseV2NativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_V2_CHANGE_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native v2 minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error(`Native ${NATIVE_V2_CHANGE_SCHEMA} minimum_runtime_version must be 2`); + } + return { + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision: positiveInteger(root.revision, "Native v2 revision"), + ...parseChangeFields(root, V2_CHANGE_KEYS) + }; +} +function parseNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA || root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const previous = root.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? parseLegacyNativeChangeValue(root) : parseV2NativeChangeValue(root); + throw new NativeSchemaMigrationRequiredError(previous.name, previous.schema); + } + throw new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + typeof root.minimum_runtime_version === "number" ? root.minimum_runtime_version : null + ); + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native ${root.schema} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger(root.revision, "Native revision"); + const fields = parseChangeFields(root, CURRENT_CHANGE_KEYS); + const approvalHash = approvedContractHash(root.approved_contract_hash); + if (fields.approval === null && approvalHash !== null) { + throw new Error("Native approved_contract_hash requires an approval"); + } + return { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + ...fields, + approved_contract_hash: approvalHash, + implementation_scope: contentAddressedRef( + root.implementation_scope, + "Native implementation_scope", + "scopes" + ), + verification_evidence: contentAddressedRef( + root.verification_evidence, + "Native verification_evidence", + "verifications" + ), + partial_allowance: contentAddressedRef( + root.partial_allowance, + "Native partial_allowance", + "allowances" + ) + }; +} +function inspectNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + const state2 = parseLegacyNativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: 1, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const state2 = parseV2NativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: state2.minimum_runtime_version, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + const minimumRuntimeVersion2 = typeof root.minimum_runtime_version === "number" && Number.isSafeInteger(root.minimum_runtime_version) ? root.minimum_runtime_version : null; + return { + status: "runtime-incompatible", + schema: typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion: minimumRuntimeVersion2, + state: null, + message: new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion2 + ).message + }; + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + return { + status: "runtime-incompatible", + schema: root.schema, + minimumRuntimeVersion, + state: null, + message: new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion).message + }; + } + const state = parseNativeChangeValue(root); + return { + status: "current", + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + state + }; +} +function nativeChangeDir(paths, name) { + assertNativeName(name); + const target = path11.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, target)) throw new Error("Native change path escaped"); + return target; +} +async function hasPendingNativeSchemaMigration(paths, name) { + const file = path11.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs11.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +var NATIVE_CHANGE_DOCUMENT_MAX_BYTES = 256 * 1024; +async function readChangeDocumentFile(file, root = path11.dirname(file)) { + const ref = path11.relative(root, file).split(path11.sep).join("/"); + const source = await readNativeBoundedTextFile({ + root, + ref, + maxBytes: NATIVE_CHANGE_DOCUMENT_MAX_BYTES + }); + const document = (0, import_yaml5.parseDocument)(source.text, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid Native change file ${file}: ${document.errors[0].message}`); + } + return document.toJS(); +} +async function inspectNativeChange(paths, name) { + const file = path11.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + const inspection = inspectNativeChangeValue(await readChangeDocumentFile(file, paths.nativeRoot)); + if (inspection.state && inspection.state.name !== name) { + throw new Error(`Native change directory/name mismatch: ${name}`); + } + if (await hasPendingNativeSchemaMigration(paths, name)) { + return { + status: "migration-required", + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + state: inspection.state, + message: `Native schema migration is incomplete for ${name}; run doctor --repair` + }; + } + return inspection; +} +async function readNativeChange(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + throw new NativeSchemaMigrationRequiredError(name, inspection.schema); + } + if (inspection.status === "runtime-incompatible" || !inspection.state) { + throw new NativeRuntimeCompatibilityError(inspection.schema, inspection.minimumRuntimeVersion); + } + return inspection.state; +} + +// domains/comet-native/native-selection.ts +var NATIVE_SELECTION_MAX_BYTES = 16 * 1024; +async function readNativeSelectionRecord(paths) { + const current = await readCometCurrentSelection(paths.projectRoot); + if (current.status === "missing" || current.selection.workflow !== "native") return null; + assertNativeName(current.selection.change); + return current.selection; +} +async function resolveSelectedNativeChange(paths) { + const value = await readNativeSelectionRecord(paths); + if (!value) return null; + await readNativeChange(paths, value.change); + return value.change; +} + +// domains/comet-native/native-hook-guard.ts +function isWithin(parent, target) { + const relative = path12.relative(parent, target); + return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative); +} +function requestTargetsAreControlOnly(projectRoot, nativeRoot, request) { + return request.targets.length > 0 && request.targets.every((targetPath) => { + const target = path12.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) return true; + const relative = path12.relative(projectRoot, target).replaceAll("\\", "/"); + return relative === ".comet/config.yaml" || isWithin(nativeRoot, target); + }); +} +async function activeNativeContext(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (!config || !(config.workflows ?? [config.default_workflow]).includes("native")) return null; + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + let entries; + try { + entries = await fs12.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return { paths, changes: [] }; + throw error; + } + const changes = []; + for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry.isDirectory() || entry.isSymbolicLink()) continue; + const state = await readNativeChange(paths, entry.name); + if (!state.archived) changes.push(state); + } + return { paths, changes }; +} +async function listActiveNativeHookChanges(projectRoot) { + const context = await activeNativeContext(projectRoot); + return (context?.changes ?? []).map((change) => ({ + workflow: "native", + name: change.name, + phase: change.phase + })); +} +async function inspectNativeHookGuard(projectRoot, request, selectedChangeName) { + const context = await activeNativeContext(projectRoot); + if (!context) return { allowed: true, reason: "Native workflow is not enabled" }; + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (context.changes.length === 0) { + return { + allowed: true, + reason: requestTargetsAreControlOnly(projectRoot, context.paths.nativeRoot, request) ? "Native control artifact write" : "No Native changes exist" + }; + } + let change; + if (selectedChangeName) { + change = context.changes.find((candidate) => candidate.name === selectedChangeName); + if (!change) { + return { + allowed: false, + reason: `Selected Native change ${selectedChangeName} is missing or archived; resume /comet-native before retrying`, + workflow: "native", + change: selectedChangeName + }; + } + } else if (context.changes.length === 1) { + change = context.changes[0]; + } else { + const selectedName = await resolveSelectedNativeChange(context.paths); + change = context.changes.find((candidate) => candidate.name === selectedName); + if (!change) { + return { + allowed: false, + reason: "Multiple Native changes are active; select the change to resume before writing code", + workflow: "native" + }; + } + } + if (change.phase === "build") { + return { + allowed: true, + reason: "Native change is in Build", + workflow: "native", + phase: change.phase, + change: change.name + }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + return { + allowed: false, + reason: `Hook write target could not be determined while Native change ${change.name} is in ${change.phase}; resume /comet-native before retrying`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + let controlTarget = false; + let externalTarget = false; + for (const targetPath of request.targets) { + const target = path12.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) { + externalTarget = true; + continue; + } + const relative = path12.relative(projectRoot, target).replaceAll("\\", "/"); + if (relative === ".comet/config.yaml" || isWithin(context.paths.nativeRoot, target)) { + controlTarget = true; + continue; + } + return { + allowed: false, + reason: `Native change ${change.name} is in ${change.phase}; implementation writes are only allowed in build. Resume /comet-native to continue safely`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + return { + allowed: true, + reason: controlTarget ? "Native control artifact write" : externalTarget ? "Write target is outside the guarded project" : "No guarded write target was provided", + workflow: "native", + phase: change.phase, + change: change.name + }; +} + +// domains/comet-entry/hook-router.ts +var DEFAULT_DEPENDENCIES = { + listNative: listActiveNativeHookChanges, + listClassic: listActiveClassicHookChanges, + inspectNative: inspectNativeHookGuard, + inspectClassic: inspectClassicHookGuard +}; +function enabledWorkflows(config) { + if (!config) return ["classic"]; + return config.workflows ?? [config.default_workflow]; +} +async function resolveHookWorkflowOwner(projectRoot, dependencies = DEFAULT_DEPENDENCIES) { + const config = await readProjectConfig(projectRoot); + const enabled = enabledWorkflows(config); + let current; + try { + current = await readCometCurrentSelection(projectRoot); + } catch (error) { + return { + status: "stale", + code: "selection-unreadable", + reason: error instanceof Error ? error.message : String(error) + }; + } + if (current.status === "selected") { + const selection = current.selection; + if (!enabled.includes(selection.workflow)) { + return { + status: "stale", + code: "workflow-disabled", + reason: `selected workflow '${selection.workflow}' is not enabled for this project` + }; + } + let selectedCandidates; + try { + selectedCandidates = selection.workflow === "native" ? await dependencies.listNative(projectRoot) : await dependencies.listClassic(projectRoot); + } catch (error) { + return { + status: "stale", + code: "change-state-unreadable", + reason: `cannot safely enumerate active Comet changes: ${error instanceof Error ? error.message : String(error)}` + }; + } + const owner = selectedCandidates.find((candidate) => candidate.name === selection.change); + if (!owner) { + return { + status: "stale", + code: "target-missing", + reason: `selected ${selection.workflow} change '${selection.change}' is missing or archived` + }; + } + if (selection.workflow === "classic") { + const resolved = await resolveCurrentChange(projectRoot); + if (resolved.status !== "selected") { + return { + status: "stale", + code: "classic-selection-invalid", + reason: resolved.status === "stale" ? resolved.reason : `selected Classic change '${selection.change}' is no longer active` + }; + } + } + return { status: "owned", owner }; + } + let native; + let classic; + try { + [native, classic] = await Promise.all([ + enabled.includes("native") ? dependencies.listNative(projectRoot) : Promise.resolve([]), + enabled.includes("classic") ? dependencies.listClassic(projectRoot) : Promise.resolve([]) + ]); + } catch (error) { + return { + status: "stale", + code: "change-state-unreadable", + reason: `cannot safely enumerate active Comet changes: ${error instanceof Error ? error.message : String(error)}` + }; + } + const candidates = [...native, ...classic]; + if (candidates.length === 0) return { status: "none" }; + if (candidates.length === 1) return { status: "inferred", owner: candidates[0] }; + return { status: "ambiguous", candidates }; +} +async function inspectCometHook(projectRoot, request, dependencies = DEFAULT_DEPENDENCIES) { + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + try { + const resolution = await resolveHookWorkflowOwner(projectRoot, dependencies); + if (resolution.status === "none") { + return { allowed: true, reason: "No active Comet change" }; + } + if (resolution.status === "stale") { + return { + allowed: false, + reason: `${resolution.reason}. Resume /comet-native or /comet-classic and select the current change before retrying` + }; + } + if (resolution.status === "ambiguous") { + return { + allowed: false, + reason: `Multiple active Comet changes require one current selection: ${resolution.candidates.map((candidate) => `${candidate.workflow}:${candidate.name}`).join(", ")}` + }; + } + const owner = resolution.owner; + return owner.workflow === "native" ? dependencies.inspectNative(projectRoot, request, owner.name) : dependencies.inspectClassic(projectRoot, owner.name, request); + } catch (error) { + return { + allowed: false, + reason: `Comet Hook Router failed closed: ${error instanceof Error ? error.message : String(error)}` + }; + } +} + +// domains/comet-entry/hook-router-entry.ts +var USAGE = "Usage: comet-hook-router --platform [--project-root ]"; +function parseArgs(args) { + let platformId; + let projectRoot; + for (let index = 0; index < args.length; index++) { + const arg = args[index]; + if (arg === "--platform") { + platformId = args[++index]; + continue; + } + if (arg === "--project-root") { + projectRoot = args[++index]; + continue; + } + throw new Error(`Unknown argument: ${arg}`); + } + if (!platformId || platformId.startsWith("--")) throw new Error("--platform is required"); + if (!COMET_HOOK_PLATFORM_IDS.has(platformId)) { + throw new Error(`unsupported Hook platform: ${platformId}`); + } + if (projectRoot?.startsWith("--")) throw new Error("--project-root requires a value"); + return { platformId, ...projectRoot ? { projectRoot: path13.resolve(projectRoot) } : {} }; +} +async function projectRootFrom(parsed) { + if (parsed.projectRoot) return parsed.projectRoot; + const discovered = await discoverNativeProject(process.cwd()); + for (const marker of [[".comet", "config.yaml"], [".git"], ["openspec", "changes"]]) { + try { + await fs13.lstat(path13.join(discovered, ...marker)); + return discovered; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + } + let cursor = path13.resolve(process.cwd()); + while (true) { + try { + await fs13.lstat(path13.join(cursor, "openspec", "changes")); + return cursor; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const parent = path13.dirname(cursor); + if (parent === cursor) break; + cursor = parent; + } + return null; +} +async function runCometHookRouter(args) { + let parsed; + try { + parsed = parseArgs(args); + } catch (error) { + process.stderr.write(`${error instanceof Error ? error.message : String(error)} +${USAGE} +`); + return 64; + } + let decision; + try { + const projectRoot = await projectRootFrom(parsed); + decision = projectRoot ? await inspectCometHook(projectRoot, readCometHookRequest()) : { allowed: true, reason: "No Comet project discovered" }; + } catch (error) { + decision = { + allowed: false, + reason: `Comet Hook Router failed closed during project discovery: ${error instanceof Error ? error.message : String(error)}` + }; + } + const output = renderCometHookDecision(parsed.platformId, decision); + if (output.stdout) process.stdout.write(output.stdout); + if (output.stderr) process.stderr.write(output.stderr); + return output.exitCode; +} +process.exitCode = await runCometHookRouter(process.argv.slice(2)); +export { + projectRootFrom, + runCometHookRouter +}; diff --git a/.build-and-verify/.gitignore b/.build-and-verify/.gitignore new file mode 100644 index 0000000..9b59974 --- /dev/null +++ b/.build-and-verify/.gitignore @@ -0,0 +1,5 @@ +/cache/ +/runs/ +/backups/ +__pycache__/ +*.py[cod] diff --git a/.build-and-verify/config.json b/.build-and-verify/config.json new file mode 100644 index 0000000..8c518b5 --- /dev/null +++ b/.build-and-verify/config.json @@ -0,0 +1,72 @@ +{ + "version": 1, + "build": { + "checks": [ + { + "id": "build.node", + "command": "npm run build", + "inputs": [ + "package.json", + "package-lock.json", + "tsconfig.json", + "index.ts", + "src", + "tests" + ] + } + ] + }, + "verify": { + "maxParallel": 1, + "timeoutSeconds": 300, + "checks": [ + { + "id": "verify.node-check", + "command": "npm run check", + "paths": [ + "index.ts", + "src/**", + "tests/**", + "tool-display-api-consumer.*", + "package.json", + "package-lock.json", + "tsconfig.json" + ], + "inputs": [ + "index.ts", + "src", + "tests", + "tool-display-api-consumer.js", + "tool-display-api-consumer.d.ts", + "package.json", + "package-lock.json", + "tsconfig.json" + ] + }, + { + "id": "verify.node-contract-local", + "command": "npm run test:contract:local", + "paths": [ + "index.ts", + "src/**", + "tests/real-runtime-contract.test.ts", + "tests/support/**", + "tests/runtime-matrix.json", + "package.json", + "package-lock.json", + "tsconfig.json" + ], + "inputs": [ + "index.ts", + "src", + "tests/real-runtime-contract.test.ts", + "tests/support", + "tests/runtime-matrix.json", + "package.json", + "package-lock.json", + "tsconfig.json" + ] + } + ] + } +} diff --git a/.build-and-verify/runtime/build_and_verify.py b/.build-and-verify/runtime/build_and_verify.py new file mode 100644 index 0000000..81b8622 --- /dev/null +++ b/.build-and-verify/runtime/build_and_verify.py @@ -0,0 +1,292 @@ +"""Build and Verify Plugin(构建与验证插件)命令入口。""" + +from __future__ import annotations + +import argparse +import importlib.util +import json +import os +import shutil +import sys +from datetime import datetime +from pathlib import Path +from types import ModuleType + + +_RUNNER_MODULE: ModuleType | None = None +RUNTIME_FILES = ("build_and_verify.py", "build_and_verify_runner.py") +VERSION_FILE = "version.json" +DEFAULT_CONFIG = {"version": 1, "build": {"checks": []}, "verify": {"checks": []}} +DEFAULT_GITIGNORE = "/cache/\n/runs/\n/backups/\n" + + +def _skill_root() -> Path: + return Path(__file__).resolve().parents[1] + + +def _templates_root() -> Path: + return _skill_root() / "assets" / "templates" + + +def _runtime_root() -> Path: + return Path(__file__).resolve().parent + + +def _runtime_target(project: Path) -> Path: + return project / ".build-and-verify" / "runtime" + + +def _plugin_root() -> Path: + return _skill_root().parents[1] + + +def _runtime_metadata() -> dict[str, str]: + version_path = _runtime_root() / VERSION_FILE + if version_path.is_file(): + try: + data = json.loads(version_path.read_text(encoding="utf-8")) + version = str(data.get("runtime_version") or data.get("plugin_version") or "unknown") + return { + "plugin": "build-and-verify", + "plugin_version": str(data.get("plugin_version") or version), + "runtime_version": version, + } + except json.JSONDecodeError: + pass + manifest_path = _plugin_root() / ".codex-plugin" / "plugin.json" + if manifest_path.is_file(): + try: + version = str(json.loads(manifest_path.read_text(encoding="utf-8")).get("version")) + return { + "plugin": "build-and-verify", + "plugin_version": version, + "runtime_version": version, + } + except json.JSONDecodeError: + pass + return { + "plugin": "build-and-verify", + "plugin_version": "unknown", + "runtime_version": "unknown", + } + + +def _version_key(version: str) -> tuple[int, ...]: + parts: list[int] = [] + for part in version.split("."): + if not part.isdigit(): + break + parts.append(int(part)) + return tuple(parts) + + +def _user_runtime_roots() -> list[Path]: + home = os.environ.get("USERPROFILE") or os.environ.get("HOME") + if not home: + return [] + root = Path(home) + return [ + root / ".codex" / "plugins" / "cache", + root / ".claude" / "plugins" / "cache", + root / ".claude" / "plugins", + ] + + +def _newer_user_runtime() -> tuple[Path, str, str] | None: + current = _runtime_metadata().get("runtime_version", "unknown") + current_key = _version_key(current) + if not current_key: + return None + best: tuple[Path, str, str] | None = None + for root in _user_runtime_roots(): + if not root.is_dir(): + continue + for version_path in root.glob("**/skills/build-and-verify/scripts/version.json"): + script = version_path.with_name("build_and_verify.py") + if not script.is_file() or script.resolve() == Path(__file__).resolve(): + continue + try: + data = json.loads(version_path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + continue + if data.get("plugin") != "build-and-verify": + continue + installed = str(data.get("runtime_version") or data.get("plugin_version") or "") + installed_key = _version_key(installed) + if installed_key <= current_key: + continue + if best is None or installed_key > _version_key(best[2]): + best = (script, current, installed) + return best + + +def _print_runtime_update_hint(project: Path) -> None: + newer = _newer_user_runtime() + if newer is None: + return + script, current, installed = newer + print(f"runtime_outdated: repository={current} installed={installed}") + print(f"run: python {script} update-runtime --project {project}") + + +def _copy_runtime(project: Path) -> None: + target = _runtime_target(project) + target.mkdir(parents=True, exist_ok=True) + for filename in RUNTIME_FILES: + source = _runtime_root() / filename + destination = target / filename + if source.resolve() != destination.resolve(): + shutil.copyfile(source, destination) + (target / VERSION_FILE).write_text( + json.dumps(_runtime_metadata(), ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + + +def _load_config_file(path: Path) -> dict: + try: + data = json.loads(path.read_text(encoding="utf-8")) + except FileNotFoundError: + raise ValueError(f"missing_config_file: {path}") from None + except json.JSONDecodeError as error: + raise ValueError(f"invalid_config_file: {path}: {error.msg}") from None + if not isinstance(data, dict): + raise ValueError(f"invalid_config_file: {path}: root must be object") + return data + + +def _merge_gitignore(path: Path) -> None: + lines = path.read_text(encoding="utf-8").splitlines() if path.exists() else [] + for entry in DEFAULT_GITIGNORE.splitlines(): + if entry not in lines: + lines.append(entry) + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def _backup_config(config_target: Path, project: Path) -> Path: + backup_dir = project / ".build-and-verify" / "backups" + backup_dir.mkdir(parents=True, exist_ok=True) + backup = backup_dir / f"config-{datetime.now().strftime('%Y%m%d-%H%M%S')}.json" + shutil.copy2(config_target, backup) + return backup + + +def _runner() -> ModuleType: + global _RUNNER_MODULE + if _RUNNER_MODULE is not None: + return _RUNNER_MODULE + runner_path = Path(__file__).resolve().with_name("build_and_verify_runner.py") + spec = importlib.util.spec_from_file_location("build_and_verify_runner", runner_path) + if spec is None or spec.loader is None: + raise RuntimeError(f"missing_runner: {runner_path}") + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + _RUNNER_MODULE = module + return module + + +def _init_project( + project: Path, + *, + config: Path | None = None, + overwrite: bool = False, +) -> int: + config_target = project / ".build-and-verify" / "config.json" + gitignore_target = project / ".build-and-verify" / ".gitignore" + runtime_target = _runtime_target(project) + try: + confirmed_config = DEFAULT_CONFIG if config is None else _load_config_file(config) + except ValueError as error: + print(str(error), file=sys.stderr) + return 1 + + # Preflight before mkdir/copy so a failed init does not create framework artifacts. + if not overwrite or config is None: + for target in [config_target, gitignore_target, runtime_target]: + if target.exists(): + print(f"existing_file: {target.relative_to(project).as_posix()}", file=sys.stderr) + return 1 + + project.mkdir(parents=True, exist_ok=True) + config_target.parent.mkdir(parents=True, exist_ok=True) + backup = _backup_config(config_target, project) if config_target.exists() else None + _merge_gitignore(gitignore_target) + config_target.write_text( + json.dumps(confirmed_config, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + _copy_runtime(project) + (project / ".build-and-verify" / "cache").mkdir(parents=True, exist_ok=True) + + if backup is not None: + print(f"backup: {backup.relative_to(project).as_posix()}") + print("status: initialized") + return 0 + + +def _update_runtime(project: Path) -> int: + _copy_runtime(project) + print("status: runtime-updated") + return 0 + + +def _build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(prog="build_and_verify.py") + subparsers = parser.add_subparsers(dest="command", required=True) + init_parser = subparsers.add_parser("init") + init_parser.add_argument("--project", required=True) + init_parser.add_argument("--config") + init_parser.add_argument("--overwrite", action="store_true") + update_parser = subparsers.add_parser("update-runtime") + update_parser.add_argument("--project", required=True) + build_parser = subparsers.add_parser("build") + build_parser.add_argument("--project", default=".") + verify_parser = subparsers.add_parser("verify") + verify_parser.add_argument("--project", default=".") + verify_parser.add_argument("--full", action="store_true") + verify_parser.add_argument("--performance-report", action="store_true") + return parser + + +def main(argv: list[str] | None = None) -> int: + parser = _build_parser() + try: + args = parser.parse_args(sys.argv[1:] if argv is None else argv) + if ( + args.command == "verify" + and args.performance_report + and not args.full + ): + parser.error("--performance-report requires --full") + except SystemExit as error: + return int(error.code) if isinstance(error.code, int) else 2 + if args.command == "init": + return _init_project( + Path(args.project).resolve(), + config=Path(args.config).resolve() if args.config else None, + overwrite=bool(args.overwrite), + ) + if args.command == "update-runtime": + return _update_runtime(Path(args.project).resolve()) + if args.command == "build": + project = Path(args.project).resolve() + _print_runtime_update_hint(project) + return int(_runner().run_build(project)) + if args.command == "verify": + project = Path(args.project).resolve() + _print_runtime_update_hint(project) + return int( + _runner().run_verify( + project, + full=args.full, + performance_report=args.performance_report, + runtime_version=_runtime_metadata()["runtime_version"], + ) + ) + parser.error(f"unsupported command: {args.command}") + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.build-and-verify/runtime/build_and_verify_runner.py b/.build-and-verify/runtime/build_and_verify_runner.py new file mode 100644 index 0000000..e41e83b --- /dev/null +++ b/.build-and-verify/runtime/build_and_verify_runner.py @@ -0,0 +1,943 @@ +from __future__ import annotations + +import concurrent.futures +import dataclasses +import fnmatch +import hashlib +import importlib.util +import json +import os +import platform +import re +import shlex +import subprocess +import sys +import threading +import time +from datetime import datetime, timezone +from pathlib import Path +from typing import Any +from collections.abc import Callable + + +FRAMEWORK_VERSION = "0.1.0" +CACHE_VERSION = "1" +DEFAULT_CHECK_TIMEOUT_SECONDS = 300 +PYTEST_XDIST_AUTO_WORKERS_ENV = "PYTEST_XDIST_AUTO_NUM_WORKERS" +DEFAULT_PYTEST_XDIST_AUTO_NUM_WORKERS = "4" +Runner = Callable[..., subprocess.CompletedProcess[Any]] + + +class ConfigError(Exception): + pass + + +@dataclasses.dataclass +class CheckResult: + index: int + check: dict[str, Any] + returncode: int + stdout: str = "" + stderr: str = "" + duration_seconds: float = 0.0 + cache_key: str | None = None + + +def _is_non_empty_string(value: Any) -> bool: + return isinstance(value, str) and bool(value.strip()) + + +def _is_non_empty_string_list(value: Any) -> bool: + return isinstance(value, list) and all( + _is_non_empty_string(item) for item in value + ) + + +def _command_tokens(command: Any) -> list[str]: + if isinstance(command, str): + try: + return shlex.split(command) + except ValueError: + return command.split() + if isinstance(command, list): + return [str(item) for item in command] + return [] + + +def _uses_pytest(command: Any) -> bool: + tokens = _command_tokens(command) + has_pytest = any(token == "pytest" or token.endswith("/pytest") or token.endswith("\\pytest") for token in tokens) + has_pytest_module = any( + token == "-m" and index + 1 < len(tokens) and tokens[index + 1] == "pytest" + for index, token in enumerate(tokens) + ) + return has_pytest or has_pytest_module + + +def uses_pytest_xdist(command: Any) -> bool: + tokens = _command_tokens(command) + has_xdist_flag = any( + token == "-n" + or (token.startswith("-n") and len(token) > 2) + or token == "--numprocesses" + or token.startswith("--numprocesses=") + for token in tokens + ) + return has_xdist_flag and _uses_pytest(command) + + +def _pytest_xdist_workers(value: Any) -> str | None: + if value is None: + return None + if value == "auto": + return "auto" + if isinstance(value, bool) or not isinstance(value, int) or value <= 0: + raise ConfigError('pytestXdistWorkers must be "auto" or positive integer') + return str(value) + + +def _string_command_token_spans(command: str) -> list[tuple[str, int]]: + spans: list[tuple[str, int]] = [] + for match in re.finditer(r'''"[^"]*"|'[^']*'|\S+''', command): + raw = match.group(0) + token = ( + raw[1:-1] + if len(raw) >= 2 and raw[0] == raw[-1] and raw[0] in {'"', "'"} + else raw + ) + spans.append((token, match.end())) + return spans + + +def _command_with_pytest_xdist_workers(command: Any, workers: str | None) -> Any: + if workers is None or not _uses_pytest(command) or uses_pytest_xdist(command): + return command + if isinstance(command, list): + tokens = [str(item) for item in command] + for index, token in enumerate(tokens): + if token == "pytest" or token.endswith("/pytest") or token.endswith("\\pytest"): + return tokens[: index + 1] + ["-n", workers] + tokens[index + 1 :] + if token == "-m" and index + 1 < len(tokens) and tokens[index + 1] == "pytest": + insert_at = index + 2 + return tokens[:insert_at] + ["-n", workers] + tokens[insert_at:] + if isinstance(command, str): + tokens = _string_command_token_spans(command) + for index, (token, end) in enumerate(tokens): + if token == "pytest" or token.endswith("/pytest") or token.endswith("\\pytest"): + return f"{command[:end]} -n {workers}{command[end:]}" + if ( + token == "-m" + and index + 1 < len(tokens) + and tokens[index + 1][0] == "pytest" + ): + end = tokens[index + 1][1] + return f"{command[:end]} -n {workers}{command[end:]}" + return command + + +def _pytest_xdist_env(workers: str | None) -> dict[str, str] | None: + if workers != "auto" or os.environ.get(PYTEST_XDIST_AUTO_WORKERS_ENV): + return None + env = os.environ.copy() + env[PYTEST_XDIST_AUTO_WORKERS_ENV] = DEFAULT_PYTEST_XDIST_AUTO_NUM_WORKERS + return env + + +def _dependency_error(check: dict[str, Any]) -> str | None: + command = check.get("command") + workers = _pytest_xdist_workers(check.get("pytestXdistWorkers")) + needs_xdist = uses_pytest_xdist(command) or (workers is not None and _uses_pytest(command)) + if needs_xdist and importlib.util.find_spec("xdist") is None: + return ( + f"missing_dependency: {check.get('id')}: pytest-xdist is required " + "for pytest xdist workers; install requirements-dev.txt\n" + ) + return None + + +def _load_config(project: Path) -> dict[str, Any]: + config_path = project / ".build-and-verify" / "config.json" + try: + config = json.loads(config_path.read_text(encoding="utf-8")) + except FileNotFoundError: + raise ConfigError("missing_config: .build-and-verify/config.json") from None + except json.JSONDecodeError as error: + raise ConfigError( + f"invalid_config: .build-and-verify/config.json: {error.msg}" + ) from None + if not isinstance(config, dict): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: root must be object" + ) + for section in ("build", "verify"): + section_config = config.get(section, {}) + if not isinstance(section_config, dict): + raise ConfigError( + f"invalid_config: .build-and-verify/config.json: {section} must be object" + ) + if section == "verify": + max_parallel = section_config.get("maxParallel") + if max_parallel is not None and ( + isinstance(max_parallel, bool) + or not isinstance(max_parallel, int) + or max_parallel < 0 + ): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + "verify.maxParallel must be non-negative integer" + ) + checks = section_config.get("checks", []) + if not isinstance(checks, list): + raise ConfigError( + f"invalid_config: .build-and-verify/config.json: {section}.checks must be list" + ) + seen_ids: set[str] = set() + for index, check in enumerate(checks): + if not isinstance(check, dict): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}] must be object" + ) + check_id = check.get("id") + if not _is_non_empty_string(check_id): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].id must be non-empty string" + ) + if check_id in seen_ids: + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].id must be unique" + ) + seen_ids.add(check_id) + command = check.get("command") + if not ( + _is_non_empty_string(command) or _is_non_empty_string_list(command) + ): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].command must be non-empty string or list of non-empty strings" + ) + if "parallel" in check: + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].parallel is no longer supported; use checkParallel" + ) + if "checkParallel" in check and not isinstance(check.get("checkParallel"), bool): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].checkParallel must be boolean" + ) + try: + workers = _pytest_xdist_workers(check.get("pytestXdistWorkers")) + except ConfigError as error: + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].{error}" + ) from None + if workers is not None and not _uses_pytest(command): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].pytestXdistWorkers requires pytest command" + ) + for field in ("paths", "inputs"): + value = check.get(field) + if value is not None and not _is_non_empty_string_list(value): + raise ConfigError( + "invalid_config: .build-and-verify/config.json: " + f"{section}.checks[{index}].{field} must be list of non-empty strings" + ) + return config + + +def _normalize_path(path: str | Path) -> str: + return Path(path).as_posix().strip("/") + + +def _dedupe(items: list[str]) -> list[str]: + seen: set[str] = set() + deduped: list[str] = [] + for item in items: + normalized = _normalize_path(item) + if normalized and normalized not in seen: + seen.add(normalized) + deduped.append(normalized) + return deduped + + +def _git_names(project: Path, *args: str) -> list[str] | None: + result = subprocess.run( + ["git", *args], + cwd=project, + check=False, + text=True, + capture_output=True, + ) + if result.returncode != 0: + return None + return result.stdout.splitlines() + + +def _git_status_names(project: Path) -> list[str] | None: + result = subprocess.run( + ["git", "status", "--porcelain=v1", "-z", "--untracked-files=all"], + cwd=project, + check=False, + text=True, + capture_output=True, + ) + if result.returncode != 0: + return None + entries = result.stdout.split("\0") + names: list[str] = [] + index = 0 + while index < len(entries): + entry = entries[index] + index += 1 + if not entry: + continue + status = entry[:2] + path = entry[3:] + if status[0] in {"R", "C"}: + # In porcelain v1 -z mode, rename/copy entries are destination then source. + if path: + names.append(path) + index += 1 + continue + if path: + names.append(path) + return names + + +def _is_excluded_relative(relative: str) -> bool: + parts = relative.split("/") + return ( + ".git" in parts + or "__pycache__" in parts + or relative == ".build-and-verify/cache" + or relative.startswith(".build-and-verify/cache/") + ) + + +def _all_project_files(project: Path) -> list[str]: + files: list[str] = [] + for root, dirs, names in os.walk(project): + root_path = Path(root) + kept_dirs: list[str] = [] + for name in dirs: + relative = (root_path / name).relative_to(project).as_posix() + if not _is_excluded_relative(relative): + kept_dirs.append(name) + dirs[:] = kept_dirs + for name in names: + path = root_path / name + relative = path.relative_to(project).as_posix() + if not _is_excluded_relative(relative): + files.append(relative) + return sorted(files) + + +def _changed_files(project: Path) -> list[str]: + status_names = _git_status_names(project) + if status_names is not None: + return _dedupe(status_names) + commands = [ + ("diff", "--name-only", "--cached"), + ("diff", "--name-only"), + ("ls-files", "--others", "--exclude-standard"), + ] + names: list[str] = [] + any_git_command_succeeded = False + for command in commands: + result = _git_names(project, *command) + if result is None: + continue + any_git_command_succeeded = True + names.extend(result) + if not any_git_command_succeeded: + names = _all_project_files(project) + return _dedupe(names) + + +def _path_matches(pattern: str, changed_file: str) -> bool: + raw_pattern = str(pattern).replace("\\", "/").strip() + directory_pattern = raw_pattern.endswith("/") + pattern = raw_pattern.strip("/") + changed_file = _normalize_path(changed_file) + if not pattern: + return False + if pattern.endswith("/**"): + prefix = pattern[:-3].rstrip("/") + return changed_file == prefix or changed_file.startswith(prefix + "/") + if directory_pattern: + prefix = pattern.rstrip("/") + return changed_file == prefix or changed_file.startswith(prefix + "/") + if any(char in pattern for char in "*?["): + return fnmatch.fnmatch(changed_file, pattern) + if "/" in pattern: + return changed_file == pattern or changed_file.startswith(pattern.rstrip("/") + "/") + return changed_file == pattern + + +def _selected_checks( + checks: list[dict[str, Any]], changed_files: list[str] +) -> list[dict[str, Any]]: + selected: list[dict[str, Any]] = [] + for check in checks: + paths = check.get("paths") or [] + if not paths: + # Pathless checks are global checks in default verify mode. + if changed_files: + selected.append(check) + continue + if any(_path_matches(pattern, changed) for pattern in paths for changed in changed_files): + selected.append(check) + return selected + + +def _stable_json(data: Any) -> str: + return json.dumps(data, ensure_ascii=True, sort_keys=True, separators=(",", ":")) + + +def _hash_file(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as file: + for chunk in iter(lambda: file.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def _is_relative_to_project(project: Path, path: Path) -> bool: + try: + path.resolve().relative_to(project.resolve()) + except ValueError: + return False + return True + + +def _validate_project_relative_input(project: Path, input_path: str) -> tuple[str, Path]: + raw_path = Path(input_path) + relative = _normalize_path(input_path) + if ( + raw_path.is_absolute() + or str(input_path).replace("\\", "/").startswith("/") + or raw_path.anchor + or ".." in Path(relative).parts + ): + raise ValueError(f"invalid_input_path: {input_path}") + path = project / relative + if not _is_relative_to_project(project, path): + raise ValueError(f"invalid_input_path: {input_path}") + return relative, path + + +def _validate_check_inputs(project: Path, check: dict[str, Any]) -> None: + for input_path in check.get("inputs") or []: + _validate_project_relative_input(project, input_path) + + +def _hash_input(project: Path, input_path: str) -> dict[str, Any]: + relative, path = _validate_project_relative_input(project, input_path) + if not path.exists(): + return {"path": relative, "missing": True} + if path.is_file(): + return {"path": relative, "type": "file", "sha256": _hash_file(path)} + if path.is_dir(): + files: list[dict[str, str]] = [] + for root, dirs, names in os.walk(path): + root_path = Path(root) + kept_dirs: list[str] = [] + for name in dirs: + child_relative = (root_path / name).relative_to(project).as_posix() + if not _is_excluded_relative(child_relative): + kept_dirs.append(name) + dirs[:] = kept_dirs + for name in sorted(names): + file_path = root_path / name + child_relative = file_path.relative_to(project).as_posix() + if _is_excluded_relative(child_relative): + continue + if not _is_relative_to_project(project, file_path): + raise ValueError(f"invalid_input_path: {child_relative}") + files.append({"path": child_relative, "sha256": _hash_file(file_path)}) + return {"path": relative, "type": "directory", "files": sorted(files, key=lambda item: item["path"])} + return {"path": relative, "type": "other"} + + +def _default_cache_inputs(project: Path, paths: list[str]) -> list[str]: + matched: list[str] = [] + for pattern in paths: + normalized = _normalize_path(pattern) + if Path(pattern).anchor or ".." in Path(normalized).parts: + raise ValueError(f"invalid_input_path: {pattern}") + matched.extend( + relative + for relative in _all_project_files(project) + if _path_matches(normalized, relative) + ) + return _dedupe(matched) + + +def _cache_key( + project: Path, + config: dict[str, Any], + check: dict[str, Any], + changed_files: list[str] | None = None, +) -> str: + if "inputs" in check and check.get("inputs") is not None: + inputs = check.get("inputs") or [] + else: + paths = check.get("paths") or [] + if paths: + inputs = _default_cache_inputs(project, paths) + else: + inputs = changed_files if changed_files is not None else _changed_files(project) + payload = { + "cache_version": CACHE_VERSION, + "framework_version": FRAMEWORK_VERSION, + "python_version": platform.python_version(), + "check_id": check.get("id"), + "command": check.get("command"), + "inputs": [_hash_input(project, item) for item in inputs], + "config": hashlib.sha256(_stable_json(config).encode("utf-8")).hexdigest(), + } + return hashlib.sha256(_stable_json(payload).encode("utf-8")).hexdigest() + + +def _cache_path(project: Path, key: str) -> Path: + return project / ".build-and-verify" / "cache" / f"{key}.json" + + +def _cache_load(project: Path, key: str) -> bool: + path = _cache_path(project, key) + if not path.is_file(): + return False + try: + data = json.loads(path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return False + return data.get("status") == "passed" + + +def _cache_store(project: Path, key: str, check: dict[str, Any]) -> None: + path = _cache_path(project, key) + path.parent.mkdir(parents=True, exist_ok=True) + temp = path.with_name(f".{path.name}.{os.getpid()}.{threading.get_ident()}.tmp") + try: + temp.write_text( + _stable_json({"status": "passed", "id": check.get("id")}) + "\n", + encoding="utf-8", + ) + temp.replace(path) + except Exception: + temp.unlink(missing_ok=True) + raise + + +def _format_seconds(value: float) -> str: + return str(int(value)) if value == int(value) else str(value) + + +def _check_timeout_seconds(config: dict[str, Any], check: dict[str, Any]) -> float | None: + timeout = check.get("timeoutSeconds") + verify_config = config.get("verify") + if timeout is None and isinstance(verify_config, dict): + timeout = verify_config.get("timeoutSeconds") + if timeout is None: + return float(DEFAULT_CHECK_TIMEOUT_SECONDS) + if isinstance(timeout, bool) or not isinstance(timeout, (int, float)) or timeout <= 0: + raise ValueError(f"invalid_timeoutSeconds: {check.get('id')}") + return float(timeout) + + +def _run_check(project: Path, check: dict[str, Any], runner: Runner) -> int: + command = check.get("command") + if not command: + print(f"missing_command: {check.get('id')}", file=sys.stderr) + return 1 + dependency_error = _dependency_error(check) + if dependency_error is not None: + print(dependency_error, end="", file=sys.stderr) + return 1 + workers = _pytest_xdist_workers(check.get("pytestXdistWorkers")) + command = _command_with_pytest_xdist_workers(command, workers) + use_shell = isinstance(command, str) + run_kwargs = { + "cwd": project, + "check": False, + "text": True, + "capture_output": True, + "shell": use_shell, + } + env = _pytest_xdist_env(workers) + if env is not None: + run_kwargs["env"] = env + try: + result = runner(command, **run_kwargs) + except FileNotFoundError: + executable = command[0] if isinstance(command, list) else str(command) + print(f"command_not_found: {check.get('id')}: {executable}", file=sys.stderr) + return 1 + if result.stdout: + print(result.stdout, end="") + if result.stderr: + print(result.stderr, end="", file=sys.stderr) + return int(result.returncode) + + +def _run_check_result( + index: int, + project: Path, + check: dict[str, Any], + config: dict[str, Any], + changed_files: list[str], + runner: Runner, +) -> CheckResult: + started_at = time.monotonic() + try: + key = _cache_key(project, config, check, changed_files) + timeout_seconds = _check_timeout_seconds(config, check) + except ValueError as error: + return CheckResult( + index, + check, + 1, + stderr=f"{error}\n", + duration_seconds=time.monotonic() - started_at, + ) + + command = check.get("command") + if not command: + return CheckResult( + index, + check, + 1, + stderr=f"missing_command: {check.get('id')}\n", + duration_seconds=time.monotonic() - started_at, + cache_key=key, + ) + dependency_error = _dependency_error(check) + if dependency_error is not None: + return CheckResult( + index, + check, + 1, + stderr=dependency_error, + duration_seconds=time.monotonic() - started_at, + cache_key=key, + ) + workers = _pytest_xdist_workers(check.get("pytestXdistWorkers")) + command = _command_with_pytest_xdist_workers(command, workers) + use_shell = isinstance(command, str) + run_kwargs = { + "cwd": project, + "check": False, + "text": True, + "capture_output": True, + "shell": use_shell, + } + env = _pytest_xdist_env(workers) + if env is not None: + run_kwargs["env"] = env + if timeout_seconds is not None: + run_kwargs["timeout"] = timeout_seconds + try: + result = runner(command, **run_kwargs) + except FileNotFoundError: + executable = command[0] if isinstance(command, list) else str(command) + return CheckResult( + index, + check, + 1, + stderr=f"command_not_found: {check.get('id')}: {executable}\n", + duration_seconds=time.monotonic() - started_at, + cache_key=key, + ) + except subprocess.TimeoutExpired: + return CheckResult( + index, + check, + 1, + stderr=f"check_timeout: {check.get('id')} exceeded {_format_seconds(timeout_seconds or 0)}s\n", + duration_seconds=time.monotonic() - started_at, + cache_key=key, + ) + except Exception as error: + return CheckResult( + index, + check, + 1, + stderr=( + f"parallel_check_exception: {check.get('id')}: " + f"{type(error).__name__}: {error}\n" + ), + duration_seconds=time.monotonic() - started_at, + cache_key=key, + ) + return CheckResult( + index, + check, + int(result.returncode), + stdout=result.stdout or "", + stderr=result.stderr or "", + duration_seconds=time.monotonic() - started_at, + cache_key=key, + ) + + +def _checks(config: dict[str, Any], section: str) -> list[dict[str, Any]]: + return list(config.get(section, {}).get("checks", [])) + + +def _max_parallel_checks(config: dict[str, Any], parallel_count: int) -> int: + verify_config = config.get("verify") + configured = verify_config.get("maxParallel") if isinstance(verify_config, dict) else None + if isinstance(configured, bool): + return parallel_count + if configured == 0: + return parallel_count + if isinstance(configured, int) and configured > 0: + return min(parallel_count, configured) + return min(parallel_count, os.cpu_count() or 1) + + +def _check_ids(checks: list[dict[str, Any]]) -> str: + return ", ".join(str(check.get("id")) for check in checks) + + +def _config_error(error: ConfigError) -> int: + print(str(error), file=sys.stderr) + print("status: failed") + return 1 + + +def _run_scheduled_checks( + project: Path, + config: dict[str, Any], + selected: list[dict[str, Any]], + changed_files: list[str], + runner: Runner, +) -> tuple[int, list[str], list[CheckResult]]: + indexed_selected = list(enumerate(selected)) + parallel_checks = [(index, check) for index, check in indexed_selected if check.get("checkParallel") is True] + serial_checks = [(index, check) for index, check in indexed_selected if check.get("checkParallel") is not True] + results: list[CheckResult] = [] + if parallel_checks: + max_workers = _max_parallel_checks(config, len(parallel_checks)) + executor = concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) + interrupted = False + try: + futures = { + executor.submit(_run_check_result, index, project, check, config, changed_files, runner): ( + index, + check, + ) + for index, check in parallel_checks + } + for future in futures: + index, check = futures[future] + try: + results.append(future.result()) + except KeyboardInterrupt as error: + interrupted = True + results.append( + CheckResult( + index, + check, + 1, + stderr=( + f"parallel_check_interrupted: {check.get('id')}: " + f"KeyboardInterrupt: {error}\n" + ), + ) + ) + break + finally: + executor.shutdown(wait=not interrupted, cancel_futures=interrupted) + if not interrupted: + results.extend( + _run_check_result(index, project, check, config, changed_files, runner) + for index, check in serial_checks + ) + else: + results.extend( + _run_check_result(index, project, check, config, changed_files, runner) + for index, check in serial_checks + ) + + failures = 0 + failed_ids: list[str] = [] + ordered_results = sorted(results, key=lambda item: item.index) + for result in ordered_results: + if result.stdout: + print(result.stdout, end="") + if result.stderr: + print(result.stderr, end="", file=sys.stderr) + print(f"duration: {result.check.get('id')} seconds={result.duration_seconds:.2f}") + if result.returncode == 0: + if result.cache_key is not None: + _cache_store(project, result.cache_key, result.check) + else: + failures += 1 + failed_ids.append(str(result.check.get("id"))) + return failures, failed_ids, ordered_results + + +def _write_performance_report(path: Path, payload: dict[str, Any]) -> bool: + temp = path.with_name(".performance-report.json.tmp") + try: + path.parent.mkdir(parents=True, exist_ok=True) + temp.write_text( + json.dumps(payload, ensure_ascii=False, indent=2) + "\n", + encoding="utf-8", + ) + temp.replace(path) + except OSError: + try: + temp.unlink(missing_ok=True) + except OSError: + pass + return False + return True + + +def run_build(project: Path, runner: Runner = subprocess.run) -> int: + try: + config = _load_config(project) + except ConfigError as error: + return _config_error(error) + checks = _checks(config, "build") + failures = 0 + for check in checks: + try: + _validate_check_inputs(project, check) + except ValueError as error: + print(str(error), file=sys.stderr) + failures += 1 + continue + if _run_check(project, check, runner) != 0: + failures += 1 + print(f"checked: {_check_ids(checks)}") + if failures: + print("status: failed") + return 1 + print("status: passed") + return 0 + + +def run_verify( + project: Path, + runner: Runner = subprocess.run, + *, + full: bool = False, + performance_report: bool = False, + runtime_version: str = "unknown", +) -> int: + try: + config = _load_config(project) + except ConfigError as error: + return _config_error(error) + checks = _checks(config, "verify") + changed_files = _changed_files(project) + selected = checks if full else _selected_checks(checks, changed_files) + failures = 0 + if full: + verify_config = config.get("verify", {}) + budget_seconds = verify_config.get("fullBudgetSeconds") + if budget_seconds is not None and ( + isinstance(budget_seconds, bool) + or not isinstance(budget_seconds, int) + or budget_seconds <= 0 + ): + return _config_error( + ConfigError( + "invalid_config: .build-and-verify/config.json: " + "verify.fullBudgetSeconds must be positive integer" + ) + ) + started_at = time.monotonic() + failures, failed_ids, results = _run_scheduled_checks( + project, config, selected, changed_files, runner + ) + total_seconds = round(time.monotonic() - started_at, 2) + if len(results) == len(selected): + over_budget = ( + total_seconds > budget_seconds if budget_seconds is not None else None + ) + report_path = ( + project / ".build-and-verify" / "runs" / "performance-report.json" + ) + report_relative_path = report_path.relative_to(project).as_posix() + if performance_report or over_budget is True: + payload = { + "schemaVersion": 1, + "runtimeVersion": runtime_version, + "generatedAt": datetime.now(timezone.utc) + .replace(microsecond=0) + .isoformat() + .replace("+00:00", "Z"), + "totalSeconds": total_seconds, + "budgetSeconds": budget_seconds, + "overBudget": over_budget, + "verificationStatus": "failed" if failures else "passed", + "checks": [ + { + "id": str(result.check.get("id")), + "status": "passed" if result.returncode == 0 else "failed", + "durationSeconds": round(result.duration_seconds, 2), + } + for result in results + ], + } + if _write_performance_report(report_path, payload): + print(f"performance-report: {report_relative_path}") + else: + print( + f"performance-report-warning: {report_relative_path}", + file=sys.stderr, + ) + if over_budget is True: + exceeded_seconds = total_seconds - budget_seconds + exceeded_percent = exceeded_seconds / budget_seconds * 100 + print( + "performance-warning: " + f"totalSeconds={total_seconds:.2f} " + f"budgetSeconds={budget_seconds} " + f"exceededSeconds={exceeded_seconds:.2f} " + f"exceededPercent={exceeded_percent:.2f} " + f"report={report_relative_path}" + ) + if failed_ids: + print(f"failed: {', '.join(failed_ids)}") + print(f"checked: {_check_ids(selected)}") + print(f"full-not-run: {str(not full).lower()}") + if failures: + print("status: failed") + return 1 + print("status: passed") + return 0 + cache_misses: list[dict[str, Any]] = [] + for check in selected: + try: + key = _cache_key(project, config, check, changed_files) + except ValueError as error: + print(str(error), file=sys.stderr) + failures += 1 + continue + if _cache_load(project, key): + print(f"cache-hit: {check.get('id')}") + continue + cache_misses.append(check) + scheduled_failures, failed_ids, _ = _run_scheduled_checks( + project, config, cache_misses, changed_files, runner + ) + failures += scheduled_failures + if failed_ids: + print(f"failed: {', '.join(failed_ids)}") + print(f"checked: {_check_ids(selected)}") + print(f"full-not-run: {str(not full).lower()}") + if failures: + print("status: failed") + return 1 + print("status: passed") + return 0 diff --git a/.build-and-verify/runtime/version.json b/.build-and-verify/runtime/version.json new file mode 100644 index 0000000..012f7cb --- /dev/null +++ b/.build-and-verify/runtime/version.json @@ -0,0 +1,5 @@ +{ + "plugin": "build-and-verify", + "plugin_version": "0.1.47", + "runtime_version": "0.1.47" +} diff --git a/.claude/rules/comet-workflow-guard.md b/.claude/rules/comet-workflow-guard.md new file mode 100644 index 0000000..3e2a64b --- /dev/null +++ b/.claude/rules/comet-workflow-guard.md @@ -0,0 +1,32 @@ +# Comet 当前需求阶段规则 + +本规则是 Native 与 Classic 共用的常驻软性防线。项目可以启用两种 workflow,但一个需求只能由一个 workflow/change 管理;不得同时套用两套阶段规则。 + +## 先确定当前需求 + +每轮开始、恢复工作或怀疑上下文被压缩后,按以下顺序检查: + +1. 读取 `.comet/config.yaml`:`workflows` 表示项目启用的能力,`default_workflow` 只决定 `/comet` 的默认入口。 +2. 读取 `.comet/current-change.json`:其中的 `workflow + change` 才是当前需求所有者。 +3. selection 缺失时,只有全项目恰好一个 Comet 活跃 change 才能只读推断;存在多个候选时必须暂停并让用户选择。 +4. selection 无效、过期、跨分支失效或指向已归档 change 时停止,不得回退到 `default_workflow` 猜测。 + +Classic 旧项目没有新版配置时只按 Classic legacy fallback 处理,不得因此启用 Native。 + +## 只应用选中的阶段规则 + +| Workflow | 禁止普通实现写入 | 允许普通实现写入 | +| --- | --- | --- | +| Native | Shape、Verify、Archive | Build | +| Classic | Open、Design、Archive | Build、Verify | + +- Native 的 Verify 只运行检查并记录证据;发现实现问题时,先记录失败并通过 Native Runtime 回到 Build,再修改实现。点号开头的普通项目文件不因名称而自动成为跨阶段白名单。 +- 当前 workflow 是 Native:恢复 `/comet-native`,由 Native 状态、证据和自动推进协议继续。 +- 当前 workflow 是 Classic:恢复 `/comet-classic`,由 Classic 状态、确认点和阶段协议继续。 +- 不要把 Native change 转换成 Classic change,或反向转换;切换 workflow 必须选择另一个独立 change。 + +## Hook 约束 + +平台只应安装一个 Comet Hook Router。一次写入事件最多进入一个 workflow Guard;不得分别运行 Native 和 Classic Hook。 + +Hook 会对多文件和 patch 目标整体裁决。当前阶段不允许普通项目写入、selection 不明确、状态失效或目标无法安全判断时会失败关闭。不要绕过 Hook;按拒绝信息恢复对应 workflow 并重新选择当前 change。 diff --git a/.claude/skills/comet-any/SKILL.md b/.claude/skills/comet-any/SKILL.md new file mode 100644 index 0000000..2e9bc3f --- /dev/null +++ b/.claude/skills/comet-any/SKILL.md @@ -0,0 +1,118 @@ +--- +name: comet-any +description: "仅在用户明确调用 /comet-any,或明确要求定制 /comet-classic 五阶段流程、创建或升级由 Comet Creator 管理的 workflow Skill 时使用;不要用于一般 Skill 的编写、整理或评审。" +--- + +# Comet Any - Skill Creator + +`/comet-any` 是 Comet 的 Skill 创建向导。用户只需要描述想要的工作流;本 Skill 负责读取真实 Skill、提出方案、等待确认、生成可验证的 Comet-native Skill Bundle,并通过内部 CLI 完成 eval、review、publish readiness 和安装预览。 + +普通用户第一层只看到三种起点: + +- `基于 /comet-classic 的五阶段定制`:覆盖 `open / design / build / verify / archive` 五阶段的 Skill 编排,但不修改 `/comet-classic` 永久入口本身。 +- `创建全新 workflow Skill`:从目标和候选 Skills 生成新的 `workflow-kernel`。 +- `整理已有 Skill`:读取已有 Skill,补齐 Workflow Node、Skill Binding、Output Schema、Guardrail、Handoff、eval 和 readiness。 + +后端 Bundle、Factory、composition 仍是内部审计词;不要把它们作为普通用户的第一屏概念。 + +## 核心模型 + +所有路径都必须编译到同一种 Workflow Contract: + +- `Workflow Node`:流程中的可恢复节点,例如 `open`、`design`、`plan`、`execute`、`subagent-execute`、`review`、`verify`、`archive`。 +- `Node Responsibility`:该 Node 在 Agent workflow 中承担的职责,用来解释它为什么存在、需要产出什么、能否替换。 +- `Skill Binding`:某个 Node 的实现 Skill 或辅助 Skill。 +- `Required Skill Call`:要求 Node 内必须调用某个 Skill,不替换 Node implementation。例如 `execute` 和 `subagent-execute` 必须调用 `elementui`,`review` 必须调用 `whitebox-code-standard`。 +- `Output Schema`:Node 必须产出的文件、状态或 evidence。Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。脚本、eval、readiness 只依赖挂到 Node 的 Output Schema,不依赖 Skill 名称。 +- `Guardrail`:阻断或放行 Node 推进的检查。 +- `Handoff`:子代理或跨 Node 交接时必须带回的 evidence。 +- `workflow-protocol.json`:生成包的唯一运行事实源,kind 为 `comet-five-phase-overlay` 或 `workflow-kernel`。 + +## 受保护边界 + +`comet-five-phase-overlay` 保留 Comet Classic 五阶段主流程和 `.comet.yaml` 状态语义。普通模式下: + +- `comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`;没有 active change 或多个 active changes 时必须阻塞并请用户选择。 +- 不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态。Bundle 草稿、eval evidence 和 publish readiness 可以有自己的证据文件,但不能替代 `.comet.yaml`。 +- `control` Node 不允许 override:`open`、`execute`、`verify`、`archive`。 +- `producer` Node 可以 override:`design`、`plan`,但必须满足对应 Output Schema。 +- `handoff` 和 `guardrail` Node 可以 require / augment。 +- 用户坚持替换 control Node 时,改走高级 `workflow-kernel`,并要求重新声明 state、Output Schema 和 Guardrail。 +- 所有 Node 都必须用 responsibility 说明职责,不使用内部坐标作为用户理解流程的方式。 + +## 工作步骤 + +1. 恢复现有状态:先运行 `comet creator guide --project . --json`,展示恢复摘要和下一步。 +2. 读取项目偏好:读取 `.comet/skill-preferences.yaml`,用 `comet creator candidates --json` 发现真实本地 Skill,再用 `comet skill show --json` 读取候选的真实内容与 hash。不得只按名字推测能力。 +3. 生成方案:把用户目标表达为 Workflow Nodes、Skill Bindings、Output Schemas、Guardrails、Handoffs 和 Evidence。 +4. 展示确认页:说明每个 Node 的职责、绑定 Skill、Required Skill Call、Output Schema、可执行披露和 readiness 影响。确认页必须为每个新增 binding 或 schema 显示 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +5. 等待用户确认:未确认前不得写 Bundle draft;存在 missing / ambiguous Skill 时必须暂停。 +6. 初始化后端状态:确认后调用 `comet creator init --file --confirmed-proposal --json`。 +7. 运行创作管线并生成 Bundle:先运行 `comet creator authoring-plan --depth quick|full --json` 取得 lane DAG。按 DAG 派发 lane——wave1(`script`、`reference`、`pause-points`)在支持子代理的平台可并发(否则按依赖顺序内联),wave2(`workflow-entry`、`skill-core`)在 script 契约之后,`skill-review` 作为汇聚 barrier。每个 lane 的产出用 `comet creator authoring-record --lane --file --json` 记录(经 schema 校验;BLOCKED/NEEDS_CONTEXT 会被拒绝)。随后运行 `comet creator generate --json`:把记录的内容叶子草稿(entry/node SKILL.md、decision-points、recovery)合并进包,而确定性脊梁(protocol/scripts/manifest)保持模板化,并渲染真实审查证据。产出 entry Skill、Node Skills、`reference/workflow-protocol.json`、六个 scripts、rules、hooks 与 `comet/eval.yaml`。 +8. 验证:展示 quick/full eval 工作量,运行或记录当前 draft hash 的 eval evidence;失败、skip 或证据 hash 过期时不得进入 ready。 +9. Review / readiness:读取 `comet publish review --platform --json`,展示 `Readiness:`、`Blockers:`、`Warnings:`、`Evidence:`。 +10. Publish / install preview:人工批准后才能 publish;安装前必须先运行 preview,并展示 `No files were written`。 + +## 方案示例 + +组件库和白盒审查场景应生成类似 plan: + +```json +{ + "goal": "基于 /comet-classic 的五阶段定制,要求组件库和白盒审查。", + "skillCreatorIntent": "customize-comet", + "workflow": { + "kind": "comet-five-phase-overlay", + "name": "team-comet", + "goal": "要求组件库和白盒审查。", + "nodes": { + "execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "reason": "Use project component library during direct implementation." + } + ] + }, + "subagent-execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "scope": "handoff" + } + ] + }, + "review": { + "requiredSkillCalls": [ + { + "skill": "whitebox-code-standard", + "scope": "review" + } + ] + } + } + } +} +``` + +## 硬性规则 + +- 必须先展示方案确认页,再生成。 +- 确认页必须为每个新增 binding 或 schema 显示 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +- Required Skill Call 不替换 Node implementation。 +- producer override 必须声明 `satisfies` 的 Output Schema。 +- Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。 +- control Node 普通模式不得 override。 +- eval、review、publish readiness 必须读取同一份 `workflow-protocol.json`。 +- readiness blockers 必须阻止 publish:缺少当前 draft hash 的 eval evidence、人工 approval、required capability 或 executable disclosure 任一项都不能进入 ready。 +- 子代理 Handoff 必须要求子代理加载 Required Skill Call 并回传 evidence。 +- 脚本只读取 protocol 和 state,不把 Skill 名称当成校验依据。 +- 安装前必须询问用户,不得自动安装。 + +## 参考资料 + +- `comet-any/reference/authoring-protocol.json` +- `comet-any/reference/authored-zone-example.md` +- `comet-any/reference/bundle-authoring.md` +- `comet-any/reference/authoring-subagents.md` +- `comet-any/reference/eval-provider.md` diff --git a/.claude/skills/comet-any/reference/authored-zone-example.md b/.claude/skills/comet-any/reference/authored-zone-example.md new file mode 100644 index 0000000..0bd453b --- /dev/null +++ b/.claude/skills/comet-any/reference/authored-zone-example.md @@ -0,0 +1,105 @@ +# Authored 区质量标尺(样例) + +这是你编写的 **Authored 区**(entry 的 `## Decision Core`、node 的 `## Guidance`)的具体质量标尺。生成器把你的 Authored 区组装到确定性 **Auto 区**(frontmatter、路由表、Entry/Exit Check、证据格式、Recovery)之上。**你只写 Authored 区**。请按本样例校准,而不是写一行套话。 + +## "Auto" 与 "Authored" 的含义 + +- **Auto 区(模板,不要重写)**:不变的控制面。node 的 Auto 区包括 `## Node Goal`、`## Entry Check`、`## Skill Implementation`、`## Required Skill Calls`、`## Output Schemas`、`## Evidence Record`、`## Guardrails`、`## Exit Check`、`## Recovery`。entry 的 Auto 区包括 `## Workflow Nodes`、`## Skill Bindings`、`## Guardrails And Evidence`、`## Runtime And Recovery`。 +- **Authored 区(你写)**:Auto 区无法得知的领域决策。这是让 Skill "好用"而非"只是能跑"的关键。 + +## 样例:`substance` 节点的 Guidance 区(workflow-kernel) + +下面是 research-writer workflow 中"Research"生产者节点的 `## Guidance` 正文。注意它是**决策内容**:前提、有序的领域步骤、超出机械 Exit Check 的完成判断、Red flags。它按名引用绑定的 Skill,但不复制其正文。 + +```markdown +## Prerequisites + +- entry 的 Decision Core 已确认研究主题与范围。 +- `research-skill` 在项目 Skill 池中可解析;若缺失,先停下询问用户,不要临时替代。 + +## Steps + +1. 加载 `research-skill` 并按其发现方法处理已确认主题。当项目 Skill 定义了特定来源顺序时,不要用通用网络搜索替代。 +2. 按优先级收集来源;为每个来源记录来源、日期和你要复用的论点。不通过项目可信度门槛的来源应直接剔除,而不是标注为"偏弱"。 +3. 把发现提炼到 `notes/*.md` 笔记文件——每条独立论点一份,含逐字引用与来源指针。综合写在 writer 节点,不在这里。 +4. 记录 `research.notes.v1` 的 `summary` evidence:一段提炼 + 产出的笔记数量。 + +## Completion reasoning + +本节点完成当且仅当两条同时成立:(a) 已记录 `summary` evidence;(b) 至少一个 artifact 匹配 `notes/*.md`。不要仅仅因为步骤清单走完就退出——如果相对主题范围笔记仍偏稀疏,应继续研究而非宣布完成。Exit Check 脚本会机械地强制 artifact + evidence 要求;你的职责是判断研究是否真正充分。 + +## Red flags + +- 记录了 `summary` 却没有产出任何 `notes/*.md` 就退出(guardrail 会阻塞——不要试图绕过)。 +- 把来源原文复制进笔记却不加引用标记或来源指针。 +- 某来源仍"待核实"就推进到 Write 节点——核实属于本节点。 +- 对需要多视角的主题,仅凭单一来源就认为充分。 +``` + +用 `###` 子标题,使其嵌套在 `## Guidance` 之下。上述四段(Prerequisites / Steps / Completion reasoning / Red flags)是 `substance` 节点的预期形态。 + +## 样例:entry Decision Core(workflow-entry) + +下面是 entry SKILL.md 的 `## Decision Core` 正文。entry 是**每次调用最先读取的文件**——一个薄 Decision Core("跑 next,照做")会让整个 Skill 感觉机械。一个富 Decision Core 是 comet 级 Skill "好用"的核心。 + +Decision Core 应建模 Auto 区不处理的三件事:(1) **语义化当前节点检测**(如何判断用户在哪个 Node,而非只看脚本输出),(2) **resume 与 drift 规则**(上下文恢复或状态与文件冲突时怎么办),(3) **决策点与 Red flags**(何时暂停等用户,以及什么是假进展)。 + +```markdown +### 自动节点检测 + +**Step 0:确定当前节点与意图** + +1. 检查 workflow protocol 的有序 Node 列表。第一个未完成(无 Exit evidence 记录)的 Node 是候选当前节点。 +2. 若用户描述的工作明显属于更后面的 Node(如"验证结果"但研究尚未完成),暂停并说明:前序 Node 必须先完成。不要跳过。 +3. 若用户描述的工作属于已标记完成的更早 Node,视为纠正——重置该 Node 的完成状态并重新进入。 + +**Step 1:读取 workflow 状态** + +运行 `node "$WORKFLOW_STATE" status` 确认检测到的节点。若脚本的 `NEXT:` 输出与文件证据冲突(如脚本说 DONE 但无 artifact),以文件为准,先纠正状态再继续。 + +**Resume 规则**: +- 每次上下文恢复,重新执行 Step 0 和 Step 1。不要信任对话历史做节点检测。 +- 若状态显示某 Node 已完成但预期 artifact 缺失,视为未完成并重新进入。 +- 若用户在某个 Node 中途恢复但话题变了,确认是继续当前 Node 还是开始新的。 + +### 决策分类与决策点 + +先分类再行动:有两个或以上会改变范围、行为、风险接受或不可逆结果的合法选项才是用户决策;唯一安全下一步直接执行;缺少依赖、状态损坏或无法继续的 guard 失败是停止条件;`NEXT: manual` 只是交还控制权。只有第一类必须暂停。 + +| 情况 | 处理 | +|------|------| +| 首次调用且主题/范围明确 | 自动初始化状态并进入第一个 Node;不得为了确认已知信息而停顿 | +| 主题、范围或目标 Node 存在两个以上互斥且合法的解释 | 合并为一个问题,让用户选择;不要猜测 | +| Node 需要用户确认输出才能推进 | 记录 evidence 后停下;等待明确确认 | +| WARNING/偏差接受或不可逆发布存在真实取舍 | 只展示当前可执行选项,并记录用户选择 | + +Node guard 失败时先自动读取证据并执行唯一安全修复;若缺少依赖或状态损坏导致无法继续,报告停止条件和恢复要求。只有恢复方式存在多个会改变范围或风险的合法选项时,才升级为上表中的用户决策。 + +### Red Flags + +| Agent 想法 | 实际风险 | +|-----------|---------| +| "首次调用都应该再确认一次" | 清晰输入不需要重复批准;只有互斥解释仍会改变范围时才询问。 | +| "脚本返回 NEXT: auto,应该立即加载下一个 Skill" | `NEXT: auto` 表示 Node 完成,不是跳过确认。检查下一个 Node 是否有决策点。 | +| "guard 失败,所以让用户决定怎么办" | 先自动诊断并执行唯一安全修复;无合法动作时报告停止条件,不要发明选项。 | +| "看起来和上次一样的话题,从上次断点继续" | 始终重新读取状态。对话记忆在上下文压缩后不可靠。 | +| "Exit Check 通过了,所以工作够好了" | Exit Check 是机械的。你的职责是判断检查之外的质量——稀疏笔记、浅层分析、缺失视角,脚本抓不到。 | +``` + +此样例以精简形式建模了 comet 的 Decision Core:语义检测(Step 0 读 Node 顺序,非只看脚本输出)、状态忠实(文件优先于过期状态)、resume 规则(每次恢复重新检测)、阻塞决策点(显式表格)、Red flags("想法 → 风险"模式,抓 agent 自欺)。 + +## substance 与 delegates + +- **substance** 节点(workflow-kernel):上面的样例就是标尺。必须有富 Guidance;缺失时该节点渲染为 `AUTHORING PENDING`,Bundle 不得 ready。 +- **delegates** 节点(comet-five-phase-overlay,委托给已安装的富 Skill):富执行内容由被委托 Skill 承载,**不要复制**。但如果该节点声明了 **Required Skill Calls**(如 execute 节点要求 `elementui`),请写一段聚焦的整合说明——在被委托流程的什么时机必须加载该 Skill、它补充什么 evidence——而不是泛泛一句"加载 X"。例如要求 `elementui` 的 delegates execute 节点: + +```markdown +本节点用 `comet-build` 执行。在其流程之外,每当改动涉及组件库时加载 `elementui`,并在记录 `required-skill:execute.elementui` 检查前确认改动使用了项目认可的组件。不要重复实现 `comet-build` 已做的事。 +``` + +## 反模式(不要写) + +- 一行式 Guidance,如"运行本节点并记录 evidence。"——Auto 区已隐含此意,毫无增量。 +- 整段复制被绑定 Skill 的正文。 +- 重述路由表或 Output Schema 列表(Auto 区已有)。 +- substance 节点没有 `### Red flags`——Red flags 是大部分真实价值所在。 diff --git a/.claude/skills/comet-any/reference/authoring-subagents.md b/.claude/skills/comet-any/reference/authoring-subagents.md new file mode 100644 index 0000000..842ac01 --- /dev/null +++ b/.claude/skills/comet-any/reference/authoring-subagents.md @@ -0,0 +1,107 @@ +# Authoring Subagents 总览 + +## 核心原则 + +`/comet-any` 的创作成果应由平台原生 subagent 分工产出,再交给主会话组装和后端 CLI 记录状态。 +Claude Code、Codex、Gemini、Copilot 等平台的 subagent 调用方式不同,本参考只定义职责、输入和输出; +具体派发方式使用当前平台提供的原生 subagent 机制。 + +`reference/subagents/*.md` 是跨平台 lane brief;Claude Code custom agent 必须单独生成到平台 agent 资源,并带 `name`、`description`、`tools`、`model` frontmatter。不要把这些 portable lane brief 直接当作 platform-native custom agent 安装。 + +先读取本总览,再只把对应角色 brief 交给对应 subagent。不要把六个角色 brief 合并成一个大 prompt; +主会话负责保留全局上下文、汇总成果、调用 `comet bundle` 和 `comet publish`,subagent 只产出可审查草稿。 + +平台支持 subagent 时必须调度。没有平台 subagent 能力时,主会话可以使用同一份 brief 以内联方式完成, +但必须在用户可读摘要和 `reference/authoring-lanes.json` 中标记为 fallback。 + +所有 subagent 只返回 Markdown 成果和结构化审查结论,不得直接写入 Bundle state,不得执行候选 Skill 的脚本, +不得运行发布、安装或分发命令。CLI 状态仍由主会话维护。 + +## 角色 brief + +在用户确认 Skill Creator 方案后、运行 `comet creator generate` 或生成源码前,主会话按以下顺序读取并派发: + +1. 脚本作者 subagent:`comet-any/reference/subagents/script-author.md` +2. reference 作者 subagent:`comet-any/reference/subagents/reference-author.md` +3. workflow entry 作者 subagent:`comet-any/reference/subagents/workflow-entry-author.md` +4. Skill 核心作者 subagent:`comet-any/reference/subagents/skill-core-author.md` +5. 停顿点作者 subagent:`comet-any/reference/subagents/pause-points-author.md` +6. Skill 审查 subagent:`comet-any/reference/subagents/skill-reviewer.md` + +角色文件在本 Skill 内的相对路径是: + +- `reference/subagents/script-author.md` +- `reference/subagents/reference-author.md` +- `reference/subagents/workflow-entry-author.md` +- `reference/subagents/skill-core-author.md` +- `reference/subagents/pause-points-author.md` +- `reference/subagents/skill-reviewer.md` + +这些 subagent 的成果先落为可审查草稿,再进入 `reference/authoring-lanes.json`、`reference/skill-review.md` +和最终 Bundle draft。若任一 subagent 报告 blocking finding,必须停在草稿修复,不得继续 ready。 + +## 按 DAG 派发 + +上面的角色 brief 顺序只是创作 DAG 的线性展开,并非要求严格串行。权威 DAG 在 `reference/authoring-protocol.json` 与 `comet creator authoring-plan --depth quick|full --json`: + +- **wave1**(`script`、`reference`、`pause-points`):彼此无依赖。在支持子代理的平台上并发派发这三个。每个只拿到自己的角色 brief、通用输入和 protocol/resolved-skills 路径(文件交接,不共享历史)。 +- **wave2**(`workflow-entry`、`skill-core`):依赖 script 契约(`NEXT:`/`SKILL:` 输出)。仅在 script lane DONE 后开始。两者之间可并发。 +- **barrier**(`skill-review`):唯一同步点。仅在 wave1 与 wave2 全部 DONE 后运行;审查者必须读取所有 artifact 与 claim。 + +无论平台如何: + +- 编排遵循 DAG 依赖;只有 barrier 真正等待此前所有 lane。 +- 不支持子代理的平台,主会话按依赖顺序内联执行同样的 lane——语义完全一致,仅延迟不同。在 `reference/authoring-lanes.json` 中按 lane 记录 `dispatchMode: "subagent"` 或 `"inline"`。 +- Claude Code 可把某个 wave 的扇出委托给其 `Workflow` 工具作为可选加速器;这是实现选择,不是契约的一部分。契约是 protocol + schemas + DAG,任何平台都能解释。 +- 每个 lane 产出在下一个依赖 wave 开始前,必须经 `comet creator authoring-record --lane --file --json` 校验并记录。 + +## 通用输入 + +每个 subagent 都必须拿到同一组上下文: + +- 用户确认的目标、起点和语言。 +- `plan.json` 中的 `goal`、`workflow.kind`、`workflow.nodes`、`engineMode`、`runnerMode`,以及规范化后的 `workflow-protocol.json`。 +- `reference/resolved-skills.json` 或等价的真实 Skill 来源摘要。 +- `reference/workflow-protocol.json` 或即将写入该文件的 workflow protocol。 +- `/comet-classic` 定制场景下的受保护边界:`open / design / build / verify / archive`、`.comet.yaml`、decision point、verify-result-transition、archive-delta-sync。 +- 项目级偏好、缺失/歧义候选处理结果、偏离原因、scripts/hooks 可执行披露。 + +## 输出格式 + +每个 subagent 返回: + +```json +{ + "lane": "", + "artifacts": [ + { + "path": "reference/example.md", + "kind": "reference", + "content": "..." + } + ], + "claims": [ + { + "id": "reference:example", + "kind": "reference", + "paths": ["reference/example.md"], + "summary": "说明该成果保证了什么" + } + ], + "findings": [] +} +``` + +`claims` 是审查依据,不是装饰字段。缺少关键 claim 时,Skill 审查 subagent 必须阻塞。 + +## 派发注意事项 + +- 每次派发必须创建新的 subagent,不得继承主会话历史。主会话只提供该角色需要的 brief、输入路径和必要背景。 +- 必须显式指定 model;平台不支持 model 选择时,在 `reference/authoring-lanes.json` 记录为 platform-default。 +- 使用文件交接:主会话提供路径,不粘贴大段全文。通用输入、来源 Skill 摘要、草稿 artifact 和报告都应以路径交接。 +- 每个 subagent 只接收自己的角色 brief、通用输入和必要 artifact,不接收其他角色的完整 brief。 +- Skill 审查 subagent 必须在其他五个作者角色产出后运行,并读取所有 artifacts 与 claims。 +- 主会话可以要求某个角色返工,但返工结果仍必须回到 `reference/authoring-lanes.json` 和 `reference/skill-review.md`。 +- subagent 不能调用 `comet bundle`、`comet publish`、`comet skill`,也不能执行候选 Skill 的脚本。 +- 如果状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,主会话必须补上下文、拆小任务、换更强模型或询问用户;不得继续组装。 +- 没有平台 subagent 能力时,内联 fallback 必须保留相同的 lane、claim 和 finding 结构。 diff --git a/.claude/skills/comet-any/reference/bundle-authoring.md b/.claude/skills/comet-any/reference/bundle-authoring.md new file mode 100644 index 0000000..c327f1e --- /dev/null +++ b/.claude/skills/comet-any/reference/bundle-authoring.md @@ -0,0 +1,166 @@ +# Bundle Authoring 参考 + +`comet creator` 是 `/comet-any` 的普通 CLI 表面,背后复用 `comet bundle` 的确定性后端。普通用户不需要记忆 Bundle 子命令;Skill Creator 负责把用户确认的 Workflow Contract 写成 plan,再调用 CLI 维护状态。 + +## Workflow Contract 输入 + +新的 `plan.json` 使用 `workflow` 作为主输入: + +```json +{ + "goal": "基于 /comet-classic 的五阶段定制,要求组件库和白盒审查。", + "skillCreatorIntent": "customize-comet", + "workflow": { + "kind": "comet-five-phase-overlay", + "name": "team-comet", + "goal": "要求组件库和白盒审查。", + "nodes": { + "execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "reason": "Use project component library during direct implementation." + } + ] + }, + "subagent-execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "scope": "handoff" + } + ] + }, + "review": { + "requiredSkillCalls": [ + { + "skill": "whitebox-code-standard", + "scope": "review" + } + ] + } + } + }, + "engineMode": "deterministic", + "runnerMode": "standalone" +} +``` + +字段约定: + +- `workflow.kind`: `comet-five-phase-overlay` 或 `workflow-kernel`。 +- `workflow.nodes`: 按 Node id 覆盖或增强 Workflow Node。 +- `implementation`: 替换 Node implementation。只能用于允许 override 的 Node。 +- `requiredSkillCalls`: Required Skill Call,要求 Node 内必须调用 Skill。 +- `augmentations`: 增强 Node,不替换主 implementation。每个新增 binding 或 schema 都必须声明 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +- `satisfies`: producer override 满足的 Output Schema id。 +- `outputSchemas`: 高级 `workflow-kernel` 可以声明自定义 Output Schema。Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。 +- `customNodes[].responsibility`: 自定义 Node 必须声明它在 Agent workflow 中承担的职责。 +- `customNodes[].requiredSkillCalls`: 自定义 Node 可直接声明必须调用的 Skill,生成脚本会按 evidence 阻断。 + +## 内置 Comet Nodes + +`comet-five-phase-overlay` 内置这些 Workflow Node: + +- `comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`;没有 active change 或多个 active changes 时必须阻塞并请用户选择。 +- 不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态;overlay 脚本只能读取并推进 `.comet.yaml`,其他文件只能作为 draft、eval 或 publish evidence。 +- `open`: control,保留 Comet intake 和 `.comet.yaml` 初始化。 +- `design`: producer,可 override,但必须满足 `comet.design.v1`。 +- `plan`: producer,可 override,但必须满足 `comet.plan.v1`。 +- `execute`: control,可 require / augment,例如要求 `elementui`。 +- `subagent-execute`: Handoff,可要求子代理使用 `elementui` 并回传 evidence。 +- `review`: Guardrail,可要求 `whitebox-code-standard`。 +- `verify`: control,保留验证和分支收尾。 +- `archive`: control,保留 OpenSpec archive 和 delta sync。 + +自定义 `workflow-kernel` Node 示例: + +```json +{ + "id": "delegate-notes", + "label": "Delegate Notes", + "kind": "handoff", + "responsibility": "Delegate release note drafting and require returned evidence.", + "implementation": { + "skill": "handoff-coordinator", + "scope": "handoff" + }, + "requiredSkillCalls": [ + { + "skill": "release-notes", + "scope": "handoff" + } + ], + "operations": ["require", "augment"], + "outputSchemas": ["release.notes.v1"], + "guardrails": [ + { + "id": "handoff-returned", + "label": "Handoff returned evidence", + "validation": "semantic" + } + ] +} +``` + +## 生成物 + +Factory 必须生成: + +- entry `SKILL.md` +- 每个 Workflow Node 对应的 internal Skill +- `reference/workflow-protocol.json` +- `reference/resolved-skills.json` +- `reference/decision-points.md` +- `reference/recovery.md` +- `reference/authoring-lanes.json` +- `reference/skill-review.md` +- `scripts/workflow-state.mjs` +- `scripts/workflow-guard.mjs` +- `scripts/workflow-handoff.mjs` +- `scripts/comet-plan.mjs` +- `scripts/comet-check.mjs` +- `scripts/comet-hook-guard.mjs` +- `comet/eval.yaml` + +三个 `workflow-*.mjs` 按 workflow 契约创作;三个 `comet-*.mjs` 由 factory 确定性生成(plan 别名、必需文件检查、hook guard),同样读取 `workflow-protocol.json`。 + +`workflow-protocol.json` 是 runtime、eval、review、publish readiness 的共同事实源。 + +## CLI 生命周期 + +常用 Creator 命令: + +```bash +comet creator guide --project . --json +comet creator candidates --json +comet creator propose --file --json +comet creator init --file --confirmed-proposal --json +comet creator resolve --candidate --source --json +comet creator generate --json +comet bundle compile --platform --json +comet eval /comet/eval.yaml --quick --html +comet eval /comet/eval.yaml --full --html +comet publish review --platform --json +comet publish approve --reviewer --json +comet publish run --platform --json +comet publish distribute --platform --scope project --preview --json +``` + +`` 是 compile 后生成 Skill 的目录。eval 结果必须登记为当前 draft hash 的 eval evidence;readiness 只读取该 hash 对应的证据,旧 hash 证据只能保留作审计,不能推进 ready。 + +## Readiness + +readiness 必须检查: + +- resolved Skills 无 missing / ambiguous。 +- proposal 已确认。 +- `workflow-protocol.json` 可加载。 +- control Node 没有普通 override。 +- producer override 满足 Output Schema。 +- 每个新增 binding 或 schema 都有 `guarded`、`handoff-guarded`、`evidence-only` 或 `advisory` enforcement。 +- 当前 draft hash 的 eval evidence 已存在且未过期。 +- 人工 review 已批准当前 hash。 +- capability gap 和 executable disclosure 已展示。 + +非 JSON 输出也必须向用户展示 `Readiness:`、`Blockers:`、`Warnings:`、`Evidence:`。 diff --git a/.claude/skills/comet-any/reference/eval-provider.md b/.claude/skills/comet-any/reference/eval-provider.md new file mode 100644 index 0000000..c954f05 --- /dev/null +++ b/.claude/skills/comet-any/reference/eval-provider.md @@ -0,0 +1,66 @@ +# Eval Evidence 参考 + +## Eval 选择 + +在任何验证动作前,必须展示: + +- `quick` 的预计运行次数、覆盖组件和 token 消耗。 +- `full` 的预计运行次数、覆盖组件和 token 消耗。 +- `skip / quick / full eval` 三个选择。 + +skip、eval 失败或证据 hash 过期时不得进入 ready;也不得 publish 或 distribute。 + +普通用户的验证路径保持单一:日常评估统一走 `comet eval`。`/comet-any` 可以在内部登记 eval evidence,但不要把内部登记步骤包装成面向普通用户的替代命令。对普通用户,解释时优先使用“验证”,不要把 `Publish readiness:` 当成第一层概念。 + +## 结果记录 + +eval evidence 必须输出结构化 JSON,至少包含: + +- 当前 draft hash。 +- 覆盖的 entry Skill 和 internal Node Skill。 +- `workflow-protocol.json` hash。 +- quick 或 full 的选择、token 消耗和结果摘要。 +- Bundle 编译、安全检查和 capability evidence。 + +只有当前 draft hash 的 eval evidence 可以推进状态。旧 hash 证据可以保留在磁盘用于审计,但不能让当前 draft 进入 ready。 + +## 人工评审 + +eval 通过后仍必须人工批准。评审摘要至少包含: + +先运行 `comet publish review --platform --json`,再基于其输出展示: + +- Bundle 名称、版本、hash。 +- 多个 entry 与 internal Skill 列表。 +- `planHash`、`preferenceHash` 与 `reference/resolved-skills.json` 真实 Skill 证据,包括项目级偏好模式、required Skill、`sourceSummaries` 与“整理后的工作方式”摘要。 +- 推荐调用顺序与 `preferenceIndex`。 +- 偏离偏好顺序的项和原因。 +- `.comet/skill-preferences.yaml` 是否在 Factory 初始化后发生漂移;`advisory` 模式给出 warning,`strict` 模式阻塞。 +- 稳定组合 Skill Bundle 的 required capability set(必需能力集合)`skills/scripts/rules/hooks/references` 是否声明完整,且 `scripts/rules/hooks` 是否继续作为 required control plane。 +- 是否生成 `comet/skill.yaml`、`comet/guardrails.yaml`、`comet/checks.yaml` 与 `comet/eval.yaml`。 +- `hooks/*.yaml` 是否仅被当作 portable hook descriptor,等待 `comet publish distribute` 编译到目标平台。 +- 能力缺口和可执行披露。 +- eval 选择、token 消耗和结果摘要。 +- `Validate this Skill` 与下一步提示,让用户知道 readiness 为什么可发布或被阻塞。 + +readiness blockers 会阻止 publish。只要存在当前 draft hash 的 eval evidence 缺失、人工 approval 缺失、required capability gap 或 executable disclosure 未确认,就必须停在评审阶段,不能继续发布。 + +只有用户显式批准后,才能运行 `comet publish approve` 并发布。 + +## 分发预览 + +执行真实分发前,必须先跑 preview: + +```bash +comet publish distribute --platform --scope project --preview --json +``` + +preview 是强制检查,不是可选附加项。它应向用户展示: + +- `Install preview` +- planned files +- unsupported capability +- executable disclosures +- `No files were written` + +只有当用户确认 preview 结果后,才可以移除 `--preview` 执行真实分发。 diff --git a/.claude/skills/comet-any/reference/subagents/pause-points-author.md b/.claude/skills/comet-any/reference/subagents/pause-points-author.md new file mode 100644 index 0000000..f4fb306 --- /dev/null +++ b/.claude/skills/comet-any/reference/subagents/pause-points-author.md @@ -0,0 +1,81 @@ +# 停顿点作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +设计用户在 Skill 中真正必须选择的位置,以及跨设备断点恢复方式。必须先区分四类情况:用户决策、自动处理、停止条件和手动衔接。只有存在两个或以上会改变范围、行为、风险接受或不可逆结果的合法选项时,才创建用户停顿点;单一安全动作直接执行,缺少依赖或状态损坏只报告停止条件,手动衔接只交还控制权。真正的用户决策不能被默认推荐、历史偏好或自动推进绕过。 + +必须覆盖: + +- `reference/decision-points.md` +- `reference/recovery.md` + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- Skill Creator 方案确认页中的 `confirm-generate`、`revise-proposal`、`cancel`:这是会改变生成结果的用户决策。 +- eval 工作量选择 `skip / quick / full eval` 和安装前人工批准:只有确实存在多个合法选项时才是用户决策。 +- 当前 draft hash 的 eval evidence 缺失或过期、unresolved、ambiguous、capability gap、executable disclosure:逐项判断是可自动修复、无路可走的停止条件,还是确有多个恢复选项的用户决策;不得统一写成停顿点。 +- runner 恢复状态和跨设备恢复入口:复用仍有效的持久化选择,不得在恢复时重复询问。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、 +workflow protocol 和已有草稿。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "设计 的用户停顿点和恢复" +model: <必须显式指定 model> +prompt: + 你是停顿点作者 subagent。 + 先读取本 brief、通用输入路径、workflow protocol 路径、Skill 草稿路径和报告文件路径。 + 开始前先把候选节点分类为用户决策、自动处理、停止条件或手动衔接。如果分类所需事实缺失,先返回 NEEDS_CONTEXT。 + 不要猜测缺失选择,也不要把自动修复、guard 失败、能力缺口、单一合法动作或手动衔接伪装成用户停顿点。 + 只产出 decision-points 和 recovery 草稿,不写 Bundle state,不执行候选脚本。 + 把完整停顿点草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回草稿时先列分类表,再说明真正的用户停顿点: + +- 每个候选节点属于用户决策、自动处理、停止条件还是手动衔接,以及判定依据。 +- 每个停顿点的触发条件。 +- 用户可选择项。 +- 每个选择会进入哪个阶段。 +- 停顿点证据写入哪里。 +- 自动处理如何直接推进;停止条件如何报告恢复条件而不发明选项。 +- 恢复时如何显示当前阶段、阻塞原因、建议下一步和真实可选项,并复用已持久化选择。 + +停顿点必须适配当前 workflow protocol,不得只列 Comet 原始停顿点。 + +## 自检 + +返回前逐项检查: + +- 每个用户停顿点都有触发条件、选项、下一阶段和证据位置。 +- 每个停顿点至少有两个当前可执行的合法选项;相邻且可同时回答的选择已合并,单一合法值不会制造停顿。 +- guard 失败、确定性重试、状态对账、能力缺口和 `NEXT: manual` 已按实际情况归入自动处理、停止条件或手动衔接,而不是默认询问用户。 +- 默认推荐、历史偏好和自动推进都不能绕过必须停顿点。 +- 恢复摘要能显示当前阶段、阻塞原因、建议下一步和真实可选项,且不会重复询问仍有效的选择。 +- 跨设备恢复不依赖当前会话记忆。 +- 停顿点适配当前组合 Skill,不只是照列 Comet 原始停顿点。 + +## 必须返回的 claim + +- `pause:decision-points` +- `pause:recovery` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、未解决疑虑和恢复风险。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.claude/skills/comet-any/reference/subagents/reference-author.md b/.claude/skills/comet-any/reference/subagents/reference-author.md new file mode 100644 index 0000000..434b986 --- /dev/null +++ b/.claude/skills/comet-any/reference/subagents/reference-author.md @@ -0,0 +1,81 @@ +# reference 作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写和整理候选 Skill 的 reference 层,让审计、恢复、评审和后续维护有真实依据。 +reference 层承载证据,不把内部审计内容塞进用户可见 `SKILL.md`。 + +必须覆盖: + +- `reference/workflow-protocol.json` +- `reference/resolved-skills.json` +- `reference/composition-report.md` +- `reference/authoring-lanes.json` + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- resolved Skill 的名称、来源、description、hash、reference、rules、scripts、hooks。 +- `sourceSummaries`,必须来自真实 `SKILL.md` 正文和直接 reference。 +- 项目级偏好、缺失/歧义候选处理、偏离原因和可执行披露。 +- `/comet-classic` 定制时保留的五阶段语义。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要读取主会话历史,也不要把来源 Skill 原文整段搬进 +reference。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "整理 的 reference 证据" +model: <必须显式指定 model> +prompt: + 你是 reference 作者 subagent。 + 先读取本 brief、通用输入路径、resolved skills 路径、workflow protocol 路径和报告文件路径。 + 开始前先提出问题:如果来源证据、hash、偏好决策或可执行披露不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失来源。 + 只产出 reference 草稿和 claims,不写 Bundle state,不执行候选脚本。 + 把完整 reference 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回 reference 草稿,说明: + +- workflow protocol 中每个阶段的目标、守卫、下一阶段条件和恢复点。 +- resolved Skill 证据如何支持组合流程。 +- 组合与用户偏好哪里一致、哪里偏离、为什么偏离。 +- 哪些内容属于用户可见流程,哪些内容只保留在 reference 审计层。 + +不要把 reference 写成原 Skill 的复制粘贴;必须提炼为组合 Skill 可用的证据和协议。 + +## 自检 + +返回前逐项检查: + +- 每个 source summary 都能追溯到真实 `SKILL.md` 或直接 reference。 +- workflow protocol 能解释阶段目标、守卫、自动推进和恢复。 +- 偏好、缺失、歧义、偏离和可执行披露都有记录。 +- 用户可见流程和内部审计内容边界清楚。 +- 没有把原 Skill 全文复制进 reference。 + +## 必须返回的 claim + +- `reference:workflow-protocol` +- `reference:resolved-skills` +- `reference:composition-report` +- `reference:authoring-lanes` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、证据缺口和疑虑。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么来源、 +尝试过什么、需要主会话如何处理。 diff --git a/.claude/skills/comet-any/reference/subagents/script-author.md b/.claude/skills/comet-any/reference/subagents/script-author.md new file mode 100644 index 0000000..2fbe26a --- /dev/null +++ b/.claude/skills/comet-any/reference/subagents/script-author.md @@ -0,0 +1,84 @@ +# 脚本作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +设计候选 Skill 的脚本契约,而不是复制 Comet Classic 的脚本。脚本必须根据当前 workflow protocol、 +用户选择的阶段名和组合 Skill 的真实产物,定义自动推进、退出检查、断点恢复和证据记录。 + +必须覆盖: + +- `scripts/workflow-state.mjs` +- `scripts/workflow-guard.mjs` +- `scripts/workflow-handoff.mjs` + +factory 还会从同一份 `workflow-protocol.json` 确定性生成 `scripts/comet-plan.mjs`、`scripts/comet-check.mjs`、`scripts/comet-hook-guard.mjs`。不要重复设计这三个脚本;你的契约只针对 skill-core 作者与 entry 作者引用的 `workflow-*.mjs`。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- `reference/workflow-protocol.json` +- `plan.json` 的 `workflow.kind`、`workflow.nodes`、`engineMode`、`runnerMode`,以及规范化后的 `workflow-protocol.json` +- `reference/resolved-skills.json` +- `/comet-classic` 定制时的 `.comet.yaml` 受保护语义;`comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`,不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要读取主会话历史,也不要要求用户重新解释已经写入 +artifact 的内容。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的脚本契约" +model: <必须显式指定 model> +prompt: + 你是脚本作者 subagent。 + 先读取本 brief、通用输入路径、workflow protocol 路径、resolved skills 路径和报告文件路径。 + 开始前先提出问题:如果脚本边界、Node 完成条件、状态写入或恢复语义不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失协议。 + 不要调用 comet bundle、comet publish、comet skill,也不要执行候选 Skill 的脚本。 + 把完整脚本契约写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回脚本契约草稿,说明每个脚本: + +- 读取哪些状态。 +- 写入哪些状态和 evidence。 +- 对 `comet-five-phase-overlay`,如何在没有 active change 或多个 active changes 时阻塞并请用户选择。 +- 如何判断当前阶段目标是否完成。 +- 如何输出 `NEXT:`、`SKILL:` 和阻塞原因。 +- 如何在阶段未完成时继续停留,而不是强制退出。 +- 如何支持跨设备断点恢复。 + +退出检查必须来自当前 workflow protocol 和组合 Skill 的目标,不得照搬 Comet Classic 脚本。 + +## 自检 + +返回前逐项检查: + +- 每个脚本都有输入、输出、状态读写、evidence 写入和失败行为。 +- Node 完成条件来自 workflow protocol,而不是来自固定阶段名。 +- `NEXT:` 和 `SKILL:` 的输出条件可被 Skill 核心作者直接引用。 +- 跨设备恢复不依赖当前会话记忆。 +- 没有执行候选脚本,也没有写入 Bundle state。 + +## 必须返回的 claim + +- `script:workflow-state` +- `script:workflow-guard` +- `script:workflow-handoff` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、测试/校验说明和疑虑。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.claude/skills/comet-any/reference/subagents/skill-core-author.md b/.claude/skills/comet-any/reference/subagents/skill-core-author.md new file mode 100644 index 0000000..94cd1b3 --- /dev/null +++ b/.claude/skills/comet-any/reference/subagents/skill-core-author.md @@ -0,0 +1,129 @@ +# Skill 核心作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写 internal Node Skill 的用户可见核心内容。entry Skill 由 workflow entry 作者单独负责。目标是产出 Comet-like 的多 Node workflow, +不得把来源 Skill 简单串联,也不复制粘贴原 Skill 全文。 + +必须覆盖: + +- 每个 internal Node Skill +- `comet/skill.yaml` 中的阶段调用语义 +- 每个 `node-skill:` claim + +## 创作区(你写什么) + +生成器把每个 Node SKILL.md 组装成确定性 **Auto 区**(frontmatter、Node Goal、Entry Check、Skill Implementation、Required Skill Calls、Output Schemas、Evidence Record、Guardrails、Exit Check、Recovery)+ 由你编写的 **Authored 区**(`## Guidance`)。**你只写 Guidance 正文,不写整个文件**。主会话通过 `comet creator authoring-record --lane skill-core --file ` 记录你的产出;artifact `..//SKILL.md` 的 `content` 即 Guidance 正文。 + +质量标尺:真实的 Comet 阶段 skill(如 `comet-build/SKILL.md`);完整 substance 节点 Guidance 范例见 `reference/authored-zone-example.md`。写决策内容,不写套话。在 Guidance 内用 `###` 子标题(嵌套在 `## Guidance` 之下): + +- `### Prerequisites` — 本 Node 开始前必须成立的前提。 +- `### Steps` — 有序、领域相关的步骤;按名引用绑定的 Skill 并说明何时调用(不要复制它的正文)。 +- `### Completion reasoning` — 本 Node 真正完成的判定(超出机械的 Exit Check),以及其中的判断取舍。 +- `### Red flags` — 看似进展、实则不然的失败模式。 + +下面是"Research" substance 节点的具体摘录,展示预期深度。注意它是**决策内容**(何时停止、什么算充分、什么该拒绝),不是重述路由表或 output schema。 + +```markdown +### Prerequisites + +- entry 的 Decision Core 已确认研究主题与范围。 +- `research-skill` 在项目 Skill 池中可解析;若缺失,先停下询问用户,不要临时替代。 + +### Steps + +1. 加载 `research-skill` 并按其发现方法处理已确认主题。当项目 Skill 定义了特定来源顺序时,不要用通用网络搜索替代。 +2. 按优先级收集来源;为每个来源记录来源、日期和你要复用的论点。不通过项目可信度门槛的来源应直接剔除,而不是标注为"偏弱"。 +3. 把发现提炼到 `notes/*.md` 笔记文件——每条独立论点一份,含逐字引用与来源指针。综合写在 writer 节点,不在这里。 +4. 记录 `research.notes.v1` 的 `summary` evidence:一段提炼 + 产出的笔记数量。 + +### Completion reasoning + +本节点完成当且仅当两条同时成立:(a) 已记录 `summary` evidence;(b) 至少一个 artifact 匹配 `notes/*.md`。不要仅仅因为步骤清单走完就退出——如果相对主题范围笔记仍偏稀疏,应继续研究而非宣布完成。Exit Check 脚本会机械地强制 artifact + evidence 要求;你的职责是判断研究是否真正充分。 + +### Red flags + +- 记录了 `summary` 却没有产出任何 `notes/*.md` 就退出(guardrail 会阻塞——不要试图绕过)。 +- 把来源原文复制进笔记却不加引用标记或来源指针。 +- 某来源仍"待核实"就推进到 Write 节点——核实属于本节点。 +- 对需要多视角的主题,仅凭单一来源就认为充分。 +``` + +节点模式(来自 protocol): + +- **substance** 节点(workflow-kernel 默认):必须有富 Guidance。缺失时该节点渲染为 `AUTHORING PENDING`,Bundle 不得 ready。 +- **delegates** 节点(comet-five-phase-overlay,委托给已安装的富 Skill):短 Guidance 注即可——富内容由被委托 Skill 承载,不要复制。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- 用户确认的阶段名和可输入名字项。 +- `reference/workflow-protocol.json` 的阶段目标、`requiredSkillCalls` 与自动推进条件。 +- `reference/resolved-skills.json` 的真实 Skill 摘要。 +- 脚本作者返回的 `NEXT:`、`SKILL:`、guard 和 recovery 契约。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、 +脚本契约和 reference 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的 Skill 核心内容" +model: <必须显式指定 model> +prompt: + 你是 Skill 核心作者 subagent。 + 先读取本 brief、通用输入路径、脚本契约路径、reference 证据路径和报告文件路径。 + 开始前先提出问题:如果阶段名、必须调用的 Skill、自动推进或用户停顿点不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失流程。 + 只写 internal Node Skill 草稿,不写 entry Skill,不写 Bundle state,不执行候选脚本。 + 把完整 Skill 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回 internal Node Skill 草稿,必须体现: + +- internal Node Skill 负责单 Node 目标、必须调用的 Skill、Node 完成证据和脚本守卫。 +- 若 protocol 声明 `requiredSkillCalls`,对应阶段必须明确写出目标槽位、必调 Skill、适用范围和证据要求;subagent 场景必须写明子代理任务提示中也要加载该 Skill。 +- 阶段未达成目标时继续工作,不因为流程清单走完就退出。 +- 自动推进必须来自脚本输出的 `NEXT:` 和 `SKILL:`,而不是让 Agent 猜下一步。 +- 嵌套 Skill 调用只写 Skill 名字,不写 provider 前缀。 +- 对 `/comet-classic` 定制,保留 `open / design / build / verify / archive` 与 `.comet.yaml` 语义。 +- 对任意 Skill 组合,整理为 Comet-like 多 Node workflow。 + +禁止: + +- 复制粘贴原 Skill 全文。 +- 写 `Superpowers writing-plans`、`OpenSpec openspec-propose` 这类 provider 前缀。 +- 在中文 Skill 中混入英文流程句。 +- 把审计报告、source hash、内部 metadata 泄漏到用户可见 `SKILL.md`。 + +## 自检 + +返回前逐项检查: + +- 每个阶段都说明必须调用的 Skill、完成证据、脚本守卫和恢复入口。 +- 每个 `requiredSkillCalls` 都能在对应Node Skill 中找到明确加载指令和证据要求。 +- 自动推进引用脚本输出的 `NEXT:` 和 `SKILL:`。 +- Skill 调用只写 Skill 名字,不写 provider 前缀。 +- 中文用户可见文案没有混入英文流程句。 +- 没有复制粘贴原 Skill 全文。 + +## 必须返回的 claim + +- 每个 internal Node Skill 的 `node-skill:` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、未解决疑虑和建议返工点。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.claude/skills/comet-any/reference/subagents/skill-reviewer.md b/.claude/skills/comet-any/reference/subagents/skill-reviewer.md new file mode 100644 index 0000000..56a7245 --- /dev/null +++ b/.claude/skills/comet-any/reference/subagents/skill-reviewer.md @@ -0,0 +1,126 @@ +# Skill 审查 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +审查其他 subagent 的 artifacts 和 claims,判断候选 Skill 是否已经像 Comet 一样可用。 +审查必须给出明确结论:`Review passed` 或 blocking findings。 + +必须覆盖: + +- `reference/skill-review.md` +- `reference/authoring-lanes.json` + +## 输入 + +读取主会话提供的通用输入,以及其他五个作者角色返回的全部 artifacts、claims 和 findings。 + +使用文件交接:主会话提供路径,不粘贴大段全文。读取总览、通用输入、五个作者报告文件、artifact 路径和 +claims。不得读取主会话历史来替代 artifact 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "审查 的 Comet-like Skill 产物" +model: <必须显式指定 model> +prompt: + 你是 Skill 审查 subagent。 + 先读取本 brief、通用输入路径、五个作者报告路径、artifact 路径、claim 清单和报告文件路径。 + 审查不信任作者报告;作者报告只是 claim,必须用 artifact 和 claim 交叉验证。 + 不要告诉审查者不要标记某问题,也不要预设某问题只能是 Minor。 + 审查是只读任务,不得修改工作树、索引、HEAD 或分支状态。 + 把完整审查写入报告文件路径,并返回两个 verdict。 +``` + +## 审查方法 + +审查不信任作者报告。先看 artifact 和 claim,再判断作者声称是否成立。不得用“作者说这是刻意设计” +来降低问题严重性。 + +审查必须给两个 verdict: + +- Skill 契约符合度:是否满足用户确认的目标、workflow protocol、`requiredSkillCalls`、claim、阶段推进、脚本守卫、停顿点和恢复要求。 +- 可用性质量:是否像 Comet 一样好用,是否清晰、可恢复、可审计、不会过度暴露内部 metadata。 + +证据必须引用 artifact 路径和 claim。不能只写“看起来可以”。 + +## 阻塞条件 + +出现以下任一情况必须给出 blocking findings: + +- 缺少 `reference/skill-review.md`。 +- 缺少 `reference/authoring-lanes.json`。 +- 缺少 workflow entry 作者、脚本作者、reference 作者、Skill 核心作者或停顿点作者的关键 claim。 +- entry Skill 把阶段路线写成多个 `**立即执行:**` Node Skill,而不是通过状态脚本只路由当前阶段。 +- 缺少 `workflow-state.mjs`、`workflow-guard.mjs` 或 `workflow-handoff.mjs` 契约。 +- 包内缺少六个生成脚本中的任何一个(`workflow-state`、`workflow-guard`、`workflow-handoff`、`comet-plan`、`comet-check`、`comet-hook-guard`)。 +- workflow entry 缺失,或 Skill 核心没有 internal Node Skill。 +- 阶段推进没有通过脚本输出 `NEXT:` 和 `SKILL:` 表达。 +- workflow protocol 声明的 `requiredSkillCalls` 没有在对应Node Skill 中明确要求加载,或 subagent 槽位没有要求子代理任务提示加载该 Skill。 +- 用户停顿点缺失,或停顿点可被默认值绕过。 +- 把确定性修复、guard 失败、状态对账、能力缺口、单一合法动作或 `NEXT: manual` 默认写成用户停顿点;或把可同时回答的相邻选择拆成连续确认。 +- entry Skill 的 frontmatter description 没有说明它是托管 workflow 的入口/恢复路由,或 internal Node Skill 的 description 允许普通任务直接触发而未限定为显式调用或 entry/runtime 路由。 +- 中文 Skill 混入英文流程句。 +- 嵌套 Skill 调用使用 provider 前缀。 +- 用户可见 `SKILL.md` 泄漏生成审计章节、source hash 或内部 metadata。 +- `/comet-classic` 定制替换或删除了 `open / design / build / verify / archive`、`.comet.yaml`、decision point、verify-result-transition 或 archive-delta-sync。 +- 任意 Skill 组合缺少自动推进、脚本守卫、用户停顿点、恢复或当前 draft hash 的 eval evidence。 + +## 严重级别 + +- Critical:会让生成 Skill 不可用、不可恢复、不可审计,或破坏 `/comet-classic` 受保护语义。 +- Important:会让阶段流程、脚本守卫、停顿点、Skill 调用或证据链不可信;必须修复后才能 ready。 +- Minor:不阻塞 ready 的清晰度、命名或维护性改进。 + +## 输出要求 + +返回: + +- `reference/skill-review.md` +- `reference/authoring-lanes.json` +- `review:skill-review` +- 最终 `Review passed` 或 blocking findings。 + +输出必须包含: + +- 两个 verdict:Skill 契约符合度、可用性质量。 +- Strengths:具体说明做得好的 artifact。 +- Issues:按 Critical、Important、Minor 分组。 +- 每个 finding 的 artifact 路径、claim、问题、影响和建议修复方式。 +- `Review passed` 或 blocking findings。 + +如果存在 Critical 或 Important,不得给出 `Review passed`。如果作者状态是 `BLOCKED` 或 `NEEDS_CONTEXT`, +必须返回 blocking findings,主会话必须补上下文、拆小任务、换更强模型或询问用户;不得继续组装。 + +## 多视角审查(按 depth) + +skill-review lane 是 DAG 的 barrier,在所有创作 lane 之后运行。其裁决用 `comet creator authoring-record --lane skill-review --file ` 记录,成为真实的 `skill-review.md` / `authoring-lanes.json` 证据——绝不是硬编码的批准。 + +- `depth: full`:派发 N=3 个独立审查者,每人只持一个 lens(`contract-fit`、`usability`、`evidence-trace`、`self-consistency`),互不见彼此结论。跨审查者的 findings 去重后,仅多数确认才成立。重复直到连续 `dryThreshold` 轮无新的 Critical/Important(上限 `maxRounds`)。`self-consistency` 必须交叉校验:引用的命令可解析、声明产物 == 实际产物、无幽灵命令引用(未注册的命令名)残留。 +- `depth: quick`:单审查者、单轮。 +- 仅当无 Critical/Important 残留时 `passed` 为 `true`。Minor 记录但不阻塞。 + +记录的 review 对象必须符合此形状(由 `authoring-record` 校验): + +```json +{ + "lane": "skill-review", + "status": "DONE", + "review": { + "passed": true, + "evidenceSource": "llm-multivote", + "voters": 3, + "lenses": ["contract-fit", "usability", "evidence-trace", "self-consistency"], + "rounds": 2, + "findings": [ + { "severity": "minor", "path": "SKILL.md", "problem": "措辞小问题。", "fix": "改写。" } + ], + "reviewedAt": "2026-06-28T00:00:00.000Z" + } +} +``` + +`passed: false` 的审查是 readiness blocker(不得 publish);缺失审查是 warning(包仍带诚实的 `deterministic-check-only` 占位,绝不伪造批准)。 diff --git a/.claude/skills/comet-any/reference/subagents/workflow-entry-author.md b/.claude/skills/comet-any/reference/subagents/workflow-entry-author.md new file mode 100644 index 0000000..9cab911 --- /dev/null +++ b/.claude/skills/comet-any/reference/subagents/workflow-entry-author.md @@ -0,0 +1,99 @@ +# Workflow Entry 作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写生成 Skill 的 entry `SKILL.md`。entry 只负责入口、恢复、主路由说明和用户停顿点;不得把阶段路线写成会立即触发多个 Skill 的执行清单。 + +必须覆盖: + +- entry Skill +- workflow-state / workflow-guard / workflow-handoff 的入口说明 +- `workflow-entry` claim + +## 创作区(你写什么) + +生成器把 entry SKILL.md 组装成确定性 **Auto 区**(frontmatter、Workflow Nodes 路由表、Skill Bindings、Guardrails And Evidence、Runtime And Recovery)+ 由你编写的 **Authored 区**(`## Decision Core`)。**你只写 Decision Core 正文,不写整个文件**。主会话通过 `comet creator authoring-record --lane workflow-entry --file ` 记录;artifact `SKILL.md` 的 `content` 即 Decision Core 正文。 + +质量标尺:`comet/SKILL.md` 的 Decision Core(完整 entry Decision Core 范例见 `reference/authored-zone-example.md`)。写 agent 可读的决策规则——机械路由已由 Auto 区的 `workflow-state.mjs next` 处理,所以聚焦判断: + +- **语义化当前节点检测** — 如何判断用户在哪个 Node,而非只跑脚本。建模 comet 的 Step 0(从用户消息检测意图,检查 Node 顺序,处理"属于前序/后序 Node"的冲突)+ Step 1(读状态,文件优先于过期状态)。 +- **Resume 与 drift 规则** — 上下文恢复时怎么办(从头重新检测,永远不信任对话历史),状态说 DONE 但 artifact 缺失时怎么办,用户在 Node 中途换话题时怎么办。 +- **决策分类与决策点** — 先区分用户决策、自动处理、停止条件和手动衔接,再只为真正需要用户选择的情况建表。清晰的首次调用、可确定修复的 guard 失败、单一合法下一步和 `NEXT: manual` 都不得制造确认点。 +- **Red flags** — "agent 想法 → 实际风险"模式,抓自欺(如"用户提到了主题所以研究已确认" → 提到 ≠ 确认)。 + +没有这四个子节的 Decision Core 是 stub,不是 Decision Core。entry 是每次调用最先读取的文件——它决定了 Skill 感觉"智能"还是"机械"。 + +Auto 区的 Node 路由表仅供参考——不要复制成执行清单,不要发出多个立即 Skill 加载。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- 用户确认的目标、语言和阶段名。 +- `reference/workflow-protocol.json` 的阶段顺序、插槽、`requiredSkillCalls` 和恢复路径。 +- 脚本作者返回的 `status`、`init`、`next`、`NEXT:`、`SKILL:` 和 guard 契约。 +- `/comet-classic` 定制场景下必须保留的 open / design / build / verify / archive 边界。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、脚本契约和 reference 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的 workflow entry" +model: <必须显式指定 model> +prompt: + 你是 workflow entry 作者 subagent。 + 先读取本 brief、通用输入路径、脚本契约路径、workflow protocol 路径和报告文件路径。 + 开始前先分类用户决策、自动处理、停止条件和手动衔接;如果启动路由、恢复路径、当前阶段判定或真正的用户选择不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失流程。 + 只写 entry SKILL.md 草稿,不写 internal Node Skill,不写 Bundle state,不执行候选脚本。 + Decision Core 必须包含四个子节:### 自动节点检测(Step 0 意图检测 + Step 1 状态读取 + Resume 规则)、### 决策分类与决策点(只列真正的用户选择)、### Red Flags(agent 想法 → 实际风险表格)。不得把 guard 失败、确定性修复、单一合法动作或手动衔接列为用户决策。没有这些子节的 Decision Core 是 stub。 + 把完整 entry 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +entry 草稿必须体现: + +- 进入 Skill 后先读取 workflow 状态,不直接加载Node Skill。 +- 未启动时先初始化状态,再查询 `next`。 +- 只有脚本输出 `NEXT: auto` 和 `SKILL: ` 后,才加载这一个 Node Skill。 +- 阶段路线只能作为参考表,不能使用“立即执行”或“必须加载”这类执行指令。 +- 对 `/comet-classic` 定制,entry 必须列出必调槽位 Skill,但只能作为阶段内义务说明,不能变成 entry 立即执行清单。 +- 用户决策、自动处理、停止条件、手动衔接、恢复路径和参考文件清楚可见;只有至少两个真实合法选项时才停顿,相邻选择应合并。 +- 对 `/comet-classic` 定制,说明保留 open / design / build / verify / archive 主路径和阶段守卫。 + +禁止: + +- 在 entry `SKILL.md` 中写多个 `**立即执行:**` Node Skill。 +- 复制粘贴原 Skill 全文。 +- 写 provider 前缀。 +- 把审计报告、source hash、内部 metadata 泄漏到用户可见 `SKILL.md`。 + +## 自检 + +返回前逐项检查: + +- Decision Core 包含全部四个必须子节:自动节点检测、决策点、Red Flags、Error Handling 或 Resume Rules。 +- entry 只有一个主路由启动协议。 +- entry 没有立即加载Node Skill 的清单。 +- 阶段路线是参考,不是执行步骤。 +- 自动推进引用脚本输出的 `NEXT:` 和 `SKILL:`。 +- 清晰首次调用直接初始化;guard 失败先自动诊断或报告停止条件;`NEXT: manual` 只交还控制权;以上情况均不伪造用户决策。 +- 中文用户可见文案没有混入英文流程句。 + +## 必须返回的 claim + +- `workflow-entry` + +缺少该 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、claim 列表、未解决疑虑和建议返工点。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、尝试过什么、需要主会话如何处理。 diff --git a/.claude/skills/comet-native/SKILL.md b/.claude/skills/comet-native/SKILL.md new file mode 100644 index 0000000..a584acb --- /dev/null +++ b/.claude/skills/comet-native/SKILL.md @@ -0,0 +1,265 @@ +--- +name: comet-native +description: 当用户明确调用 /comet-native、要求启动或恢复 Native change,或入口路由到 Native 时使用;负责澄清需求、读取状态并推动 Shape → Build → Verify → Archive。 +--- + +# Comet Native + +Native 保存需求、完整目标规格、状态和证据。你负责理解、实现和验证;Runtime 负责状态、边界和恢复。 + +整个流程在本 Skill 内完成。不要加载阶段 Skill,也不要强制套用 Plan、TDD、Debug 或 Review 方法。 + +## 需求澄清协议 + +从 `.comet/config.yaml` 读取 `native.clarification_mode`。允许值为 `sequential` 和 `batch`;字段缺失时使用 `sequential`。该配置决定用户问题的组织方式和离开 Shape 前的确认契约,不改变 Native 的阶段、change schema、安全确认或调用方停点。 + +先识别会改变用户可见结果、但尚未定义的分支。“规范化”“直观”“标准”“预期”等词不是产品契约;只有用户原话、用户确认的答案,或明确适用于当前行为的公开契约可以关闭这类分支。 + +仓库现状、依赖默认值、相邻功能和行业惯例只能支持推荐,不能代替用户决定。“保持现有行为”只约束已经存在的结果,不自动定义新行为。 + +先判断分支是否只影响实现方式,而不改变任何用户可见结果。不能证明这一点时,按用户决定处理。即使用户要求“不要询问实现选择”,也不能把产品决定重新归类为实现选择。 + +能从仓库、工具或运行环境查明的事实由你负责调查,不交给用户回答。宿主支持并行工作时,可以并行调查相互独立的事实,但不能把并行能力作为流程前提。尚未查清的事实只阻塞依赖它的问题,不阻塞其他已经具备条件的问题。 + +只有共同定义同一个用户决定的多个细节才合并为一道策略问题。彼此独立的用户决定不能合并:Sequential 模式逐轮处理,Batch 模式为每项分别编号。不要为了增加问题数量制造歧义,也不要把实现选择放入用户问题清单。若一道问题仍未覆盖同一决定的某种合理解释,应拓宽这道问题,而不是另造一个依赖关系不清的问题。 + +### 提问载体 + +提问前先检查当前宿主的工具列表。当前工具列表提供 `AskUserQuestion` 时,Claude Code 下优先使用它展示结构化选项;其他宿主使用等价的用户输入工具。每个选项都要有简短标签和影响说明,推荐项在说明中标明,但不能自动代用户选中。 + +- Sequential 模式每轮提交一道结构化问题。选项互斥时使用单选;只有同一个用户决定本身允许同时选择多个彼此兼容的选项时,才使用多选。不能用一道多选题压缩多个独立的用户决定。 +- Batch 模式在整组问题都能满足当前工具的问题数、选项数和字段限制时,把本轮整组问题放在同一次调用中。不能把同一轮拆成多次工具调用,让后面的问题在用户回答前无法同时展示。 +- 当前宿主没有结构化提问工具,或 Batch 整轮无法在一次调用中完整表达时,整轮使用编号文本降级模式。文本必须保留相同的问题、选项、推荐和影响,然后停止并等待用户回复编号。 +- 若第一次调用失败或宿主报错,判定本会话结构化提问不可用,当轮改用文本降级模式,本会话后续不再重试。工具调用成功后直接等待用户作答,不再同时输出一套重复的文本问题。 + +### Sequential 模式 + +把当前目标视为一棵用户可见结果的决策树。进入 Shape、用户每次回答后,以及相关仓库事实发生变化后,都必须重新执行以下循环: + +1. 先调查当前分支依赖的仓库、工具和运行环境事实,不把可自行查明的事实问给用户。 +2. 从目标向下逐一遍历所有合理可达的用户可见分支,包括当前答案会引出的下游边界、失败结果和默认行为。不能在得到第一个可行解释后停止,也不能用推荐项静默关闭未确认分支。 +3. 在正式产物中维护可复核的未决事项及必要依赖摘要,不保存隐藏推理或完整内部推演。若仍有多个独立决定,选择前置条件已满足且最上游的一个;其他决定留到后续轮次。 +4. 在 brief 中只记录当前一道 `- [blocking] <问题>`。一次只问最上游的一个问题:每轮只询问这一个用户决定,不把多个独立决定塞进同一道多选题或多个并列问句。 +5. 给出“问题 / 推荐 / 影响”,用选项、正例或反例让差异可判断,然后结束本轮。推荐必须明确,但不能代用户选择。新答案引出的每一道下游或依赖问题也必须完整保留这三部分,不能只给一个候选集合后要求用户增删。 + +用户回答后,立即把确认内容写入 Decisions 和完整目标规格,移除当前阻塞项,再从目标开始重新遍历整棵决策树。目标规格尚不存在时,必须在处理该答案的同一轮创建;只更新 brief、等到最终确认或 Build 才补规格,都不算完成答案持久化。把答案当作产品约束,不当作获准采用的实现算法:一轮答案只能关闭该问题明确选择的输入→输出结果,不能因为某种实现会顺带处理其他反例,就把独立的空输入、失败或嵌入边界静默视为已决定。新答案引出的分支必须进入后续轮次;模糊、部分或未回答的内容继续保持 `[blocking]`。 + +当没有未决用户决定时,仍不能直接进入 Build。先进行一次完整性复核,主动寻找被遗漏或静默假设的用户可见分支;然后向用户给出包含目标、范围、关键决定、验收标准和明确非目标的共享理解摘要,并在 brief 中记录 `- [blocking] CONFIRM: <确认内容>`。这次复核也是可追溯性检查:摘要和验收示例中的每一项产品行为都必须能追溯到用户原话、某轮已确认答案或明确适用的公开契约;“一致”“直观”“通常如此”和仓库惯例不算确认。若某项策略第一次出现在摘要中,说明澄清循环尚未完成,必须把它重新标为用户问题,而不能借最终确认一次性决定。用户明确确认前,不修改项目实现、不进入 Build、也不调用 `next`。用户补充或否定时,更新正式产物并重新执行上述循环;明确确认后移除该阻塞项,记录确认,并使用 `--confirmed` 推进。 + +### Batch 模式 + +把当前目标视为一棵用户可见结果的决策树,并把尚未确定的用户决定按前置关系组织起来。只需要维护可复核的未决事项、依赖摘要和正式产物,不保存隐藏推理或完整内部推演。 + +每轮计算“本轮可回答问题集”:其中每个问题的前置决定都已确定,所需环境事实也已查清,并且答案不依赖本轮其他问题。这些问题组成当前 frontier;依赖仍未确定决定或调查中事实的问题留到后续轮次。 + +某个候选分支需要环境事实时,事实调查仍由你负责。宿主支持 sub-agent 或其他并行工作时,必须并行启动相互独立的事实调查;调查中的事实只暂缓依赖它的下游问题,其他 frontier 问题必须在当前轮继续提出。宿主不支持并行时,由当前执行者直接调查,不能把并行能力作为流程前提,也不能把可查明的事实转交用户。 + +对本轮可回答问题集执行以下步骤: + +1. 在 brief 的 Open questions 中使用 `- [blocking] Q1: <问题>`、`- [blocking] Q2: <问题>` 的固定格式分别保存各题;不要使用 Markdown 有序列表代替该前缀。 +2. 一次提出整组问题,并为每题给出“问题 / 推荐 / 影响”。编号应允许用户用“1 按推荐,2 选择 B”这类方式回答。 +3. 更新正式产物并提出问题后结束本轮,不进入 Build,也不调用 `next`。 + +使用以下格式: + +```text +1. 问题:…… + 推荐:…… + 影响:…… + +2. 问题:…… + 推荐:…… + 影响:…… +``` + +用户回答后,把已确认内容写入 Decisions 和完整目标规格,移除对应 `[blocking]`;没有回答或回答不明确的问题继续保持 `[blocking]`,不得按推荐项自行补全。随后根据新答案重新计算本轮可回答问题集,逐轮处理新出现的分支。 + +当本轮可回答问题集为空、相关事实已经查清且所有已识别用户决定均已处理时,执行一次完整性复核,重新检查是否仍有未处理或被静默假设的用户可见分支。向用户给出包含目标、范围、关键决定、验收标准和明确非目标的共享理解摘要,并在 brief 中使用 `- [blocking] CONFIRM: <确认内容>` 记录最终确认。用户明确确认前,不进入 Build,也不调用 `next`;用户补充或否定时,更新相应分支并继续下一轮。明确确认后,移除该阻塞项、记录确认,并使用 `--confirmed` 推进。 + +以文本“规范化”为例,一个完整问题应同时说明大小写折叠、外围标点、内部标点或撇号保留,并用反例展示不同选择的输出。 + +达成共享理解前,可以调查仓库事实、创建或恢复 Native change,并在 brief 中记录 `[blocking]`。不要进入 Build、修改项目实现或调用 `next`。 + +用户回答后,更新原有 change 的 brief 和完整目标规格,再重新检查是否还有用户决定。不要为补充答案创建第二个 change,也不要把未确认选项写成既定规格。 + +离开 Shape 时,只有本轮刚记录了用户对既有阻塞问题的确认,才给 `next` 传 `--confirmed`。Batch 模式的最终共享理解确认属于这类确认;用户最初提出需求不算。 + +若调用方要求在该 transition 后停下或切换会话,严格执行以下序列:更新正式产物 → 调用一次允许的 transition → transition 成功后不再调用工具 → 输出约定标记并结束本轮。即使 Runtime 返回 `continuation.disposition: continue`,也不能越过这个停点。 + +## 执行边界与状态快照 + +调用方指定停点时,只完成停点前允许的工作。下一会话重新调用 `/comet-native`,从磁盘读取 status、selection 和正式产物后继续,不依赖聊天记忆恢复进度。 + +调用方若要求保留某次状态变化前的 Runtime 返回快照,应在执行该状态变化之前,使用真实命令的机器可读模式,并通过重定向直接保存标准输出。快照确认完整后不得重建、刷新或覆盖;它只反映生成时的真实状态,不能根据最终结果补写。 + +## 开始或恢复 + +`/comet-native` 是 Skill 入口,不是 shell 命令。通过宿主的 Skill 机制调用;不要在 shell 中执行 `/comet-native`。 + +先运行 Native `status` 和 `show`。恢复 Verify 或 Archive 时,运行 `status --details`,读取有预算的验收页、详细 findings、`findingsTruncated` 和最新 checkpoint。 + +- findings 被截断时,先处理已返回项,再重新读取 details。 +- `acceptancePage.nextCursor` 非空时,按命令参考继续分页。 +- 随后读取 `.comet/config.yaml`,确定 `native.clarification_mode`,再读取 `comet-state.yaml`、brief、拟议规格、canonical 规格、仓库实现、项目规则和相关测试。 +- 磁盘与仓库事实优先于聊天记忆;能从环境取得的事实不要询问用户。 + +已有 active change 时,先只读确认哪个 change 对应当前目标。确认后显式运行: + +```text +comet native select +``` + +该命令建立项目级共享 selection。不要新增 `resume` 命令,也不要依赖 `status` 或 `show` 的读取副作用建立归属。 + +存在多个 active change,且 selection 不能唯一确定目标时,让用户选择。只有磁盘事实证明没有 active change 时,才把目标归纳为 lowercase kebab-case 名称并创建: + +```text +comet native new --language zh-CN +``` + +只使用配置指定的 `/comet/`,不扫描或修改其他工作流目录。 + +命令与 Runtime 定位见[命令参考](reference/commands.md),产物格式见[产物参考](reference/artifacts.md),中断与恢复见[恢复参考](reference/recovery.md)。自带 Runtime 位于 [scripts/comet-native-runtime.mjs](scripts/comet-native-runtime.mjs)。 + +项目只安装一份 Comet 工作流 Rule;支持 Hook 的平台只安装一个 `comet-hook-router.mjs`。Rule 与 Router 根据 `.comet/config.yaml` 和 `.comet/current-change.json` 确定当前 workflow,一次写入最多路由给一个 Guard。 + +当前 change 属于 Native 时,只应用 Native 的 Shape、Build、Verify、Archive 边界。不要同时运行 Native 与 Classic Guard,也不要用默认 workflow 猜测当前 change 的归属。Native 主流程不依赖任何外部 Skill。 + +## 决策协议 + +维护一份未决事项清单,按依赖顺序处理仍没有唯一答案的用户可见分支。重点检查: + +- 输出与默认行为; +- 边界条件与失败结果; +- 范围、风险和不可逆操作; +- 明确适用于当前行为的已有约束。 + +把关键名词或动作改写成可区分解释的“输入 → 输出”或“触发 → 结果”示例。一个反例能区分两种合理解释,就说明仍需用户决定。 + +文本或 token 行为通常要检查大小写、首尾与内部标点、空白、Unicode、空输入、重复项、顺序和并列结果。CLI 或 API 行为通常要检查默认值和错误结果。不要为了覆盖清单制造不存在的歧义。 + +对解析、计数、分词或边界识别,完整性复核还要用反例检查:空输入与纯空白、没有分隔符、连续或混合分隔符,以及看似分隔符但属于有效内容的字符(例如缩写中的句点、数字中的小数点或转义后的分隔符)。只有任务范围内的反例才需要询问;明确非目标应直接排除。若两个反例可以独立选择不同结果,就不能合并为一个用户决定。契约只覆盖“非空输入”时,不能把它当作空输入行为的依据。 + +只有用户给出的信息、明确非目标、已确认决定,或当前能力的明确公开契约可以消除分支。发现阻塞后,按需求澄清协议为当前模式计算并提出一个问题或本轮可回答问题集;回答前不要调用 `next` 或修改项目实现。 + +当未决事项清单为空,且仅凭 brief、完整目标规格、仓库事实和项目规则就能实现并验收,两种模式都先完成最终共享理解确认;Sequential 每轮仍只提出一个用户决定,Batch 仍按本轮可回答问题集成组提出。 + +## 推进契约 + +Shape、Build、Verify 的 transition 会返回 `next: auto | manual`,以及 `continuation.disposition: continue | await-user | blocked | done`、所需输入和下一动作。Archive 不通过 `next` 推进;成功归档才返回 `done`。 + +这些字段组成机器可读的 continuation 契约。`next: auto` 只表示当前 transition 已成功,不代表宿主会在后台执行后续工作。 + +收到 `next: auto` 且 disposition 为 `continue` 后,重新读取返回的 phase 和必要产物。没有用户决定或 Runtime 阻塞时,在本 Skill 内持续推进下一阶段,不等待用户再次触发。 + +若 disposition 为 `await-user`、`blocked` 或 `next: manual`,先根据磁盘事实和 blocking findings 处理。只有缺少的输入确实属于用户决定时才提问。 + +Sequential 模式的当前问题和最终共享理解确认,以及 Batch 模式中尚未回答的问题和最终共享理解确认,都保持为 `[blocking]`。它们是需要等待用户输入的正常停点,不能通过自动推进绕过。 + +`workspace-root-changed` 与 `workspace-inspection-unavailable` 是只读提示,不单独阻止推进或归档。未知 workspace finding、确定冲突、失效证据和 repair stop 必须处理。 + +长任务需要保留阶段内进度时,使用 `comet native checkpoint` 保存简短摘要、下一动作和真实产物引用。checkpoint 不推进 phase,也不替代 brief、规格或验证报告;不要另建 resume、handoff 或任务清单。 + +## Shape + +确认并写入:Outcome、Scope、Non-goals、Acceptance examples、Constraints and invariants、Decisions、Open questions、Verification expectations。阻塞问题在 brief 中标记为 `- [blocking]`;Batch 模式可以同时保存本轮全部可回答问题。 + +只有当 brief、完整目标规格、仓库事实和项目规则足以让后续执行者在不猜测用户可见行为的情况下实现并验收,Shape 才算完成。 + +- 更新 `brief.md`,使其能够约束实现与验收。 +- 用户明确给出的 lowercase kebab-case capability ID 必须原样用于 `specs//spec.md`。 +- 用户只给出显示名称时,在正文保留原名,并稳定派生 lowercase kebab-case capability ID。 +- 长期行为发生变化时,写归档后的完整目标规格,不写增量 patch。 +- 删除 capability 时运行 `comet native spec remove `;operation 和 canonical base hash 由 Runtime 推断并冻结。 +- 仍有未决事项时保留 `[blocking]` 并停下。 + +准备完成后运行: + +```text +comet native next --summary <摘要> [--confirmed] +``` + +仅在本轮刚记录用户对既有阻塞问题的确认时追加 `--confirmed`;Sequential 和 Batch 模式都必须先取得最终共享理解确认。Runtime 会把 approval 绑定到当时的 brief/spec contract hash;若 Build 中 contract 发生变化,先取得用户对当前 contract 的确认,再按 status 返回的命令重试。不要手工编辑 `approval` 或 `approved_contract_hash`。 + +## Build + +选择满足 brief 与拟议规格的最简单可靠方案。实现方式、是否保存计划、测试粒度、调试方法和审查强度由你根据风险决定。 + +不要为流程制造额外文档。发现需求或规格漂移时,先更新 Native 产物;出现新的用户决定时标记 `[blocking]`,按当前配置的澄清协议处理。Sequential 模式需要重新遍历决策树,Batch 模式需要重新计算问题集;两种模式都要在继续实现前取得更新后共享理解的最终确认。 + +完成后提供真实项目产物;没有代码变化时给出明确理由。然后运行: + +```text +comet native next --summary <摘要> --artifact <项目内路径> [--confirmed] +``` + +没有代码变化时按命令参考使用 `--no-code-reason`。Runtime 会返回 implementation scope 和首个 `acceptancePage`;保存 Runtime 派生的验收 ID,并按 `nextCursor` 读取全部页面。不要自行计算 ID。 + +Git 快照只包含 tracked 和未被 ignore 的 untracked 文件,submodule/gitlink 作为原子条目。非 Git 项目使用有界物理树快照。 + +- `git-selection-changed`:等待 Git 写入稳定后重试,不能授权为 partial scope。 +- `git-enumeration-limit`:先缩小或清理项目所有范围;只有 Runtime 返回可授权 scope,且用户接受未枚举尾部的具体风险时,才能使用 partial 协议。 +- `physical-selection-changed` 或 `physical-enumeration-limit`:等待文件系统稳定或缩小项目树后重试,不能授权为 partial scope。 + +Runtime 无法证明 scope 完整时会停在 Build,返回 partial scope hash 和未归属项。先补充真实 artifact 或消除未归属变化。确实只能接受 partial 时,说明具体缺口并取得用户确认,再使用同一个 hash: + +```text +--allow-partial-scope --partial-reason <理由> --confirmed +``` + +不要手改 snapshot、evidence 或猜测未枚举路径,也不要把 partial 写成 complete。 + +## Verify + +根据 Acceptance examples、完整目标规格和风险运行验证。记录实际命令、结果、跳过项、规格一致性、已知限制和结论;未运行的检查不能写成通过。 + +在 `verification.md` 的固定 acceptance evidence 块中逐项使用 Runtime 返回的 `acceptance_id`。每项只能记录项目相对 evidence refs,或记录诚实的 `skipped_reason`。用 `comet native evidence format` 把条目序列化成规范文本再粘贴,不要手工排版这段 JSON——手写几乎不可能逐字节匹配规范序列化规则。格式见产物参考。 + +需要一份可重建的文本卫生证据时,可运行内置只读检查: + +```text +comet native check +``` + +该命令只扫描当前 implementation scope/current snapshot 中有界的项目内普通文本文件,不调用 Git、shell、项目脚本、外部进程或外部 Skill。它不会修改项目文件、phase、Run 或 trajectory;结果写入内容寻址 receipt。它不替代按风险选择的项目测试。 + +写完报告后运行: + +```text +comet native next --summary <摘要> --result pass|fail --report verification.md [--receipt ] +``` + +fail 会回到 Build。修复后重新验证,并用 `--failure-category` 与 `--failed-check` 提交稳定、非敏感的失败事实。 + +同一失败第二次出现会告警;第三次且 scope 没有进展会停止。scope 发生真实变化会结束当前 repair episode。scope 未变化但有明确新假设时,可按 status 返回的 signature 使用一次 `--override-repair`;同一 signature 不得重复 override。达到停止条件后请用户决定,不要弱化检查或伪造 pass。 + +进入 Archive 后,brief、规格、implementation scope、报告或 receipt 发生变化会使证据失效。按 Runtime continuation 回到 Build,重新封印 scope 并验证;不要沿用失效的 pass。 + +## Archive + +状态进入 Archive 且 Verify 为 pass 后,先预演: + +```text +comet native archive --dry-run +``` + +检查 create/replace/remove、证据新鲜度、当前 Native root 内的 change 重叠和恢复状态。没有阻塞时,用本次预演返回的精确 hash 提交: + +```text +comet native archive --expect-preflight +``` + +调用方要求保存预演或提交 envelope 时,首次调用本身就使用机器可读模式并写入目标文件。提交使用已保存预演中的 hash;文件验证成功后保持不可变,不得在归档后重跑命令覆盖。 + +Runtime 会在锁内重新计算事实,发生漂移就拒绝提交。成功后更新 canonical 规格,并把 change 移入日期前缀的 archive 目录。 + +遇到 canonical 冲突时,重读并改写完整目标规格,再运行 `comet native spec rebase --summary <摘要>`。该命令会受控回到 Build;随后重新实现、确认、验证和归档。未完成事务按恢复参考处理。 + +## 不变规则 + +- 不直接编辑 `phase`、`approval`、`spec_changes`、Run state、trajectory、锁或 transaction journal。 +- 不跳过阶段检查。Shape、Build、Verify 使用 `comet native next`;Archive 使用两步预演与提交。 +- 不调用外部 Skill。Native 主流程只依赖 Comet 自带 Runtime。 +- 不保存隐藏推理,只保存摘要、产物引用、命令结果、hash、状态变化和时间戳。 +- 不把 token、密码、私钥、连接串或其他凭据写入摘要、理由与报告。 +- 没有用户决定或 Runtime 阻塞时持续推进;有用户决定时,Sequential 模式只问一个最上游问题,Batch 模式询问本轮全部可回答问题,随后等待回答。 diff --git a/.claude/skills/comet-native/reference/artifacts.md b/.claude/skills/comet-native/reference/artifacts.md new file mode 100644 index 0000000..26167e6 --- /dev/null +++ b/.claude/skills/comet-native/reference/artifacts.md @@ -0,0 +1,221 @@ +# Native 产物参考 + +## 目录 + +```text +/.comet/config.yaml +/.comet/current-change.json # Native/Classic 共享的当前需求归属 +/comet/ + specs//spec.md + changes// + comet-state.yaml + brief.md + specs/ + verification.md + runtime/ + baseline-manifest.json + workspace.json # process-free 物理 root 身份;只作提示 + run-state.json # 通过 Native Protected Run I/O 读写 + trajectory.jsonl # 有事件数、单事件和总字节预算 + pending-action.json # 可选;仅在 Run 有待处理动作时存在 + context.md # 可选;Run context ref + artifacts.json # 可选;Run artifact refs + transition.json # 可选;未完成阶段推进日志 + checkpoint-journal.json # 可选;未完成 progress checkpoint 日志 + checkpoints/ + latest.json # 最近阶段边界 checkpoint + progress.json + manifests/.json + evidence/ + scopes/.json + snapshots/.json + allowances/.json + verifications/.json + check-receipts/.json + archive/YYYY-MM-DD-/ + runtime/ + locks/ + transactions// + transaction.json + events.jsonl +``` + +`artifact-root` 由项目配置唯一指定。Native 不使用隐藏的 change 目录,也不从其他需求目录发现状态。项目级 `.comet/config.yaml` 是持久配置例外;`root move` 中断恢复期间还可能在源/目标 artifact root 旁出现 Runtime 管理的临时 staging 或 quarantine,事务收口后会清除。它们不是第二个可写 Native change 根。 + +## 项目配置 + +```yaml +schema: comet.project.v1 +default_workflow: native +workflows: + - native +native: + artifact_root: docs + language: zh-CN + clarification_mode: sequential + snapshot: + include: + - "**/*" + exclude: [] + max_files: 10000 + max_total_bytes: 268435456 + max_duration_ms: 60000 +``` + +`clarification_mode` 控制 Native 如何组织用户决定,以及离开 Shape 前采用哪条确认契约:`sequential` 每轮询问一个最上游问题,`batch` 每轮询问所有前置条件已经确定的问题。字段缺失时使用 `sequential`。它不改变 change schema、生命周期、安全确认或调用方停点。 + +`snapshot` 定义内容快照的显式范围与资源预算。`include`/`exclude` 使用项目相对 `/` 路径以及 `*`、`**`、`?`;规范化策略及 hash 会写入新 change 的 baseline。后续 current snapshot 继续使用 baseline 策略,不能靠中途修改范围隐藏实现变化。`max_files`、`max_total_bytes` 和 `max_duration_ms` 只限制捕获工作,可按仓库规模提高;文件内容使用流式 SHA-256,不依赖 Git object hash,也没有独立的 5 MiB 单文件限制。 + +根目录迁移期间会出现 runtime 管理的 `pending_root_move`。存在该字段时普通写命令必须停止,不能自行选择旧根或新根。 + +## 当前需求归属 + +```json +{ + "schema": "comet.selection.v2", + "workflow": "native", + "change": "add-sentence-counting", + "branch": null +} +``` + +`.comet/current-change.json` 是 Native 与 Classic 共用的当前需求 selection,不是 Native change 产物。Native 的 `new` 和 `select` 会写入 `workflow: native`;Hook Router 每次只把写入交给这一个 workflow 的 Guard。selection 缺失时只有全项目恰好一个 active Comet change 才能只读推断;多个候选、失效 selection 或已归档目标都会失败关闭。 + +项目配置最多 64 KiB,selection 最多 16 KiB,change YAML 最多 256 KiB。brief 与单个拟议规格最多各 1 MiB;一个 change 最多 64 个拟议规格,contract 的 brief + specs 总读取最多 4 MiB。拟议规格目录还限制可枚举条目,`show` 的序列化载荷最多 10 MiB。超过预算时 Runtime 保留原文件并失败关闭,不以静默截断替代完整需求。 + +## Change 状态 + +```yaml +schema: comet.native.v3 +minimum_runtime_version: 3 +revision: 1 +name: add-sentence-counting +language: zh-CN +phase: shape +brief: brief.md +approval: null +approved_contract_hash: null +spec_changes: + - capability: sentence-counting + operation: create + source: specs/sentence-counting/spec.md + base_hash: null +verification_result: pending +verification_report: null +implementation_scope: null +verification_evidence: null +partial_allowance: null +archived: false +created_at: 2026-07-14 +run_id: null +``` + +不要直接编辑 Runtime 管理字段。`phase`、`revision`、`approval`、`approved_contract_hash`、`spec_changes`、operation、`base_hash`、三个 evidence ref、`run_id` 和 `archived` 都由 Runtime 管理。 + +`approval: confirmed` 表示 Runtime 已记录用户对当前共享理解的明确确认。`implicit` 只用于兼容旧 change,不代表用户已经确认;处于 Build 的旧 `implicit` change 必须确认后才能进入 Verify。`approved_contract_hash` 把 approval 绑定到当时的 brief/spec contract,contract 发生变化后也必须由用户重新确认。需要改变需求时,只更新 brief 和 `specs//spec.md`;删除 capability 使用 `comet native spec remove`,再由命令检查并推进。 + +## Brief + +`brief.md` 固定使用八个一级标题: + +```text +# Outcome +# Scope +# Non-goals +# Acceptance examples +# Constraints and invariants +# Decisions +# Open questions +# Verification expectations +``` + +前四节必须有实质内容。仍阻塞实现的问题在 Open questions 下以 `- [blocking]` 开头;普通备注不会阻塞 Shape。 + +Sequential 模式的 Open questions 同时保存一个最上游阻塞问题。Batch 模式使用 `- [blocking] Q1: <问题>`、`- [blocking] Q2: <问题>` 保存本轮全部可回答问题;该无序列表前缀是 Runtime 识别阻塞的固定格式,不能改成 Markdown 有序列表。未回答项继续保持 `[blocking]`。当前模式的全部问题处理完且完整性复核通过后,两种模式都使用 `- [blocking] CONFIRM: <确认内容>` 保存共享理解确认,明确确认前不能进入 Build。 + +问题编号只服务于当前澄清轮次。已确认答案应写入 Decisions 和完整目标规格;不要新增决策树产物,也不要把隐藏推理写入 brief。 + +## 完整目标规格 + +拟议规格固定写在 `changes//specs//spec.md`,描述归档后 capability 应有的完整行为,不写只在旧文本上成立的增量片段。每个 capability 只能出现一次操作: + +| operation | canonical 现状 | source | base_hash | +| --- | --- | --- | --- | +| `create` | 必须不存在 | 必填 | `null` | +| `replace` | 必须存在 | 必填 | 当前 canonical 文件 SHA-256 | +| `remove` | 必须存在 | 禁止 | 当前 canonical 文件 SHA-256 | + +`next` 首次发现 proposed spec 时推断 create/replace 并冻结 hash;`spec remove` 为 remove 冻结 hash。归档在锁内重新计算 hash,实际值与 `base_hash` 不同表示并发变化,必须重新读取并改写完整目标规格,再用 `spec rebase` 受控刷新基线、回到 Build 并重新验证,不能覆盖或手改 hash。 + +## Verification + +`verification.md` 固定使用六个非空一级标题: + +```text +# Acceptance evidence +# Commands and results +# Skipped checks +# Spec consistency +# Known limitations and risks +# Conclusion +``` + +保存可复核事实,不保存隐藏推理文本。未运行的检查放在 Skipped checks,失败结果不能写成 pass。 + +Runtime 最多从 brief 与拟议规格合计派生 1024 个验收项,超出就拒绝继续,不会先构造无界列表再截断。`acceptancePage` 每页最多 16 项;单项文字最多 512 UTF-8 字节、context 最多 4 项且每项最多 256 字节,整页最多 32 KiB。文字或 context 截断会显式标记,验收 ID 不会因分页或截断而丢失;cursor 绑定当前 acceptance hash,契约变化后旧 cursor 会失效。 + +`# Acceptance evidence` 下必须恰好有一个固定机器块。ID 由 Runtime 从 brief/spec 派生,通过 Build 结果或 `status --details` 返回;不要自行计算或改写。生成这个块时用 `comet native evidence format` 把条目序列化成规范文本再粘贴进去,不要手工排版 JSON——手写几乎不可能逐字节匹配规范序列化规则,会被拒绝并报 "canonical serialization" 错误: + +```text + +[ + { + "acceptance_id": "acceptance-", + "evidence_refs": [ + "src/feature.ts" + ] + }, + { + "acceptance_id": "acceptance-", + "evidence_refs": [], + "skipped_reason": "该平台当前不可用。" + } +] + +``` + +数组按 `acceptance_id` 排序,`evidence_refs` 也排序。每项只能二选一:至少一个项目相对 evidence ref,或空数组加非空 `skipped_reason`。不能同时给证据和跳过理由,也不能引用绝对路径、Native 外部路径、`.git` 或 `.env*`。 + +```text +comet native evidence format [--entries ] +``` + +把上面这份条目数组(不带 markers)以 JSON 通过 stdin 传入,或用 `--entries ` 指定一个 JSON 文件;命令输出的文本已经包含 start/end markers 和规范排序、缩进,原样粘贴到 verification.md 的 `# Acceptance evidence` 一节即可。 + +## 内容寻址证据 + +- `baseline-manifest.json`:change 创建时的有界项目快照。它只记录项目相对路径、size、内容 hash、capture provider、规范化 scope policy、实际资源预算和省略事实,不保存文件内容。Git 只用于项目文件枚举和 gitlink 边界,不作为普通文件的内容身份;普通文件始终流式计算 SHA-256。Git provider 纳入 tracked 和未被 ignore 的 untracked 文件,并把 submodule/gitlink 作为原子条目;非 Git 项目使用带前后枚举围栏的有界物理树 provider。显式排除项属于 baseline 定义之外,不伪装成 omission;创建时若策略范围内仍有省略项,`new` 会失败并清理未完成 change,同时返回实际限制与支持的配置修复路径。 + - `git-selection-changed`:等待 Git 写入稳定后重试,不能授权为 partial scope。 + - `git-enumeration-limit`:先缩小或清理项目所有范围。只有 current snapshot 返回可授权 scope,且用户接受未知尾部的具体风险时,才能按精确 hash、理由与 `--confirmed` 使用 partial 协议。 + - `physical-selection-changed` 和 `physical-enumeration-limit`:稳定或缩小项目树后重试,不能授权为 partial scope。 + - 任何情况都不能手改 evidence 或猜测未枚举路径。 +- `evidence/scopes/`:Build 离开时由 baseline、当前快照、声明产物和 contract 派生的 implementation scope。当前快照不完整时不会猜测删除;变化过多时只展开有界明细,其余由带数量与内容 hash 的 `scope-detail-overflow` 表示。scope 不完整时 Runtime 停止;用户显式接受后才生成 `allowances/`。 +- `evidence/verifications/`:Verify 结论的 envelope,绑定 Runtime 身份、change revision、contract、acceptance coverage、scope、报告 hash 和可选 check receipt。任一绑定事实变化都会 stale。 +- `evidence/check-receipts/`:`comet native check` 的内置策略结果。它只保存 policy/version、scope/snapshot 绑定、有界 issue 与计数,不保存文件内容,也不是测试完整性的证明。 +- `checkpoints/`:阶段内恢复摘要与真实产物 manifest。checkpoint 会递增 revision,但不改变 phase,也不能代替 brief、规格、scope 或 verification。 + +所有 hash ref 都由 Runtime 写入并在读取时重算。不要复制旧 ref 到新状态、手改 JSON 或把 receipt 当作 pass;`next`、status 和 Archive 会重新读取并检查新鲜度。 + +Evidence retention 只能由 doctor 显式执行,不在普通工作流中后台删除文件。只读 doctor 报告候选;`doctor --repair` 只清理 active change 中至少 30 天、每种 evidence kind 最新 32 份之外,并且能从当前 state refs 及其依赖闭包证明未引用的 snapshot、scope、allowance、verification 和 check receipt。 + +候选按 dependents-before-dependencies 排序。每个文件先在父链与身份复核后改名到同目录唯一 `.gc` quarantine,再复核并删除。中断的 quarantine 会由后续 doctor 发现;显式 repair 仅在原路径不存在且内容与身份有效时无覆盖恢复。 + +出现原文件与 quarantine 冲突、多份 quarantine、归档 change、pending transition/checkpoint、缺失依赖、损坏文档、未知目录项、symlink 或其他特殊文件时,推迟或拒绝清理。不要把 retention 当作修复损坏证据的办法。 + +Build 与 Verify 使用 inspect-then-persist:先计算并校验 contract、scope、acceptance、repair、Run 与 trajectory,再把最终证据引用写入状态和 transition。Partial Build 可先内容寻址保存候选 scope 以返回稳定 hash,但没有确认时不会生成 allowance 或推进;被后续检查阻塞的 Verify 不会留下可被误认成已提交结论的 verification evidence。 + +Run state、trajectory、checkpoint、pending action、context 和 artifact refs 只能经过 Native Protected Run I/O。读取拒绝 symlink/junction、非普通文件、路径或文件身份变化和越界,并执行打开前后复核;写入在原子提交前复核父链和目标身份。 + +当前预算为:Run state 256 KiB;trajectory 8 MiB、4096 个事件、单事件 256 KiB;checkpoint 与 pending action 各 256 KiB;context 与 artifact refs 各 1 MiB。通用 Engine 存储函数不是 Native 文件边界。 + +阶段 transition journal 最多 512 KiB,baseline manifest 的硬上限为 8 MiB。Archive/root move transaction journal 最多 256 KiB;`events.jsonl` 最多 1 MiB 或 1024 个事件,单个事件最多 16 KiB。摘要、无代码理由、partial 理由、repair override 摘要与跳过理由在持久化前执行长度校验和凭据形态脱敏;不要把 token、密码、私钥或连接串写入 workflow 证据。 diff --git a/.claude/skills/comet-native/reference/commands.md b/.claude/skills/comet-native/reference/commands.md new file mode 100644 index 0000000..2f31f2b --- /dev/null +++ b/.claude/skills/comet-native/reference/commands.md @@ -0,0 +1,124 @@ +# Native 命令参考 + +优先使用已安装的 `comet native`。若宿主环境只提供 Skill 文件,使用本 Skill 的自带 runtime: + +```text +node /scripts/comet-native-runtime.mjs [options] +``` + +两种入口的参数、stdout、stderr 和退出码相同。普通发现从当前目录向上寻找 `.comet/config.yaml` 或仓库根;生成式 launcher 可附加隐藏参数 `--project-root `。 + +## 项目与根目录 + +```text +comet native init [--root ] [--language en|zh-CN] +comet native root show +comet native root move +``` + +`artifact-root` 必须是项目内相对路径,默认值是 `docs`。`.` 生成 `/comet/`,`docs` 生成 `/docs/comet/`。`init --language` 会把项目的 Native 默认语言持久化到 `.comet/config.yaml`;后续 `new` 未显式传入 `--language` 时继承该值。再次运行 `init --language` 可以改变以后新建 change 的默认语言,不改写已有 change。已有配置拒绝冲突的 `--root`;改变根目录必须使用 `root move`,不能直接改配置。 + +## Change 管理 + +```text +comet native new [--language en|zh-CN] +comet native spec remove +comet native spec rebase --summary +comet native list [--cursor ] +comet native show +comet native status [--cursor ] +comet native status [--details [--acceptance-cursor ]] +comet native select +``` + +`new` 在配置缺失时创建默认配置和 `/docs/comet/`。完整目标规格写入 `specs//spec.md`;`next` 自动推断 create/replace 并冻结 canonical hash。删除 capability 使用 `spec remove`,不要手工编辑 `spec_changes`。 + +canonical 并发变化导致冲突时,先重读并改写完整目标规格,再用 `spec rebase` 刷新 operation/hash、回到 Build 并清除原验证结论。 + +`show` 返回状态、brief 和拟议完整规格。`status` 返回有预算的阶段、证据新鲜度、finding 摘要、checkpoint、repair 状态和 continuation。`status --details` 还会返回: + +- 最多 50 条详细 findings; +- `findingsTruncated` 标记; +- 恢复细节; +- 首个 `acceptancePage`。 + +findings 被截断时,先处理已返回项,再重新读取 details。`nextCursor` 非空时,用 `--acceptance-cursor` 逐页读取,直至为 null。acceptance cursor 只允许与具体 change 和 `--details` 同用,并绑定当前 acceptance hash。 + +`status` 与 `show` 始终只读。恢复已确认的目标 change 时显式运行 `select`,不要新增 `resume` 命令。`new` 与 `select` 都会写项目级共享 `.comet/current-change.json`,并把 `workflow` 固定为 `native`;它们不会修改 Classic change。 + +`list` 与不带 change 的 `status` 返回同一种只读分页投影,每页最多 24 个 change;`nextCursor` 非空时原样传给 `--cursor`。cursor 绑定当前完整名称集合,change 增删后旧 cursor 会明确失效,不会错位分页。最多接受 4096 个可见 change,整页序列化结果不超过 512 KiB。`show` 还会限制规格数量、单文件、累计读取和最终输出大小;超限时拒绝,不截断需求正文。 + +## 阶段内进度与内置检查 + +```text +comet native checkpoint \ + --summary \ + --next-action \ + [--artifact ]... \ + [--expect-revision ] + +comet native check +``` + +`checkpoint` 只保存同阶段摘要、下一动作和内容寻址的产物 manifest;它通过 revision/CAS 防止覆盖,不改变 phase。`check` 只允许在 Verify 且已有 implementation scope 时运行 Comet 内置的有界只读文本扫描。它不调用 Git、shell、项目脚本、外部 Skill 或任何外部进程,不接受任意命令、路径、环境或超时参数,也不修改项目文件或 change/Run/trajectory;结果、issue 计数和 scope 新鲜度会写入独立的内容寻址 receipt。检查发现问题或 stale 返回 1,但 receipt 仍会落盘。 + +```text +comet native evidence format [--entries ] +``` + +写 verification.md 的 `# Acceptance evidence` 机器块前,用这个命令把条目数组序列化成规范 Markdown 文本再粘贴,不要手工排版 JSON。默认从 stdin 读取条目 JSON,也可用 `--entries ` 指定文件;输出已包含 start/end markers、固定排序与缩进,原样粘贴即可通过 `next --result` 的规范序列化校验。 + +## 阶段推进 + +```text +comet native next --summary \ + [--confirmed] \ + [--artifact ]... \ + [--no-code-reason ] \ + [--allow-partial-scope --partial-reason --confirmed] \ + [--result pass|fail] \ + [--report ] \ + [--receipt ] \ + [--failure-category ]... \ + [--failed-check ]... \ + [--override-repair --override-summary ] + +comet native archive --dry-run +comet native archive --expect-preflight +``` + +- Shape:brief 和拟议规格通过后推进。Sequential 与 Batch 都必须取得最终共享理解确认并传 `--confirmed`。成功进入 Build 时,Runtime 会把 confirmed approval 绑定到当前 contract hash。 +- Build:重新检查 brief 和拟议规格;至少给出一个真实项目产物,或使用 `--no-code-reason`。旧 change 若仍为 `approval: implicit`,必须先确认当前共享理解;若 contract 在 approval 后变化,status/next 会要求用户重新确认当前 contract。两种情况都只有取得确认后才传 `--confirmed`。无法证明完整 scope 时,第一次调用返回 scope hash 与有界未归属明细而不推进;超出明细预算的变化由 `scope-detail-overflow` 的数量与内容 hash 表示。只有用户接受具体风险后,才可用完全匹配的 `--allow-partial-scope`、理由与 `--confirmed` 重试。 +- Verify:必须提供 `--result` 和完整 `--report`;可选 `--receipt` 必须是当前 change、revision、contract 与 implementation scope 上 fresh 的内置 receipt。fail 回到 Build,可用失败分类和检查 ID 形成无进展签名;pass 进入 Archive。 +- Repair:第三次相同失败会返回 manual stop。scope 真正变化时普通 Build `next` 会结束旧 repair episode 并继续;scope 不变时只能用 status 返回的 signature 和非空摘要 override 一次。单个 episode 的 semantic repair budget 与已耗尽 override 不可绕过;通用 Run iteration 只提供事件序号,不是长期 change 的永久停止条件。 +- Archive:只能由 `archive` 命令完成,不能用 `next` 代替。先 `--dry-run`,再把同一次预演返回的 `preflightHash` 原样传给 `--expect-preflight`;Runtime 在锁内重算后才提交。 + +## 诊断与恢复 + +```text +comet native doctor [] +comet native doctor [] --repair +comet native doctor [] --repair [--strategy continue|rollback] +``` + +只读 doctor 不改文件。`--repair` 只处理可证明安全的 selection、陈旧锁、evidence retention、普通阶段 transition、workspace 身份修复和确定性事务恢复;用户编写的 YAML、Markdown 与规格不会被自动重写。 + +`--strategy` 是可选的事务恢复参数,不是普通 repair 的必填项。普通 transition 只支持 `continue`,不支持 `rollback`。 + +doctor 也会只读报告 evidence retention 候选。显式 `--repair` 只清理 active change 中至少 30 天、每种 evidence kind 最新 32 份之外、且依赖闭包证明未引用的派生 evidence/receipt;归档证据、当前状态引用、依赖项、较新文件和每类最新 32 份始终保留。删除按 dependents-before-dependencies 排序,并先进入同目录 quarantine;中断后只读 doctor 报告 recovery required,显式 repair 在无覆盖且身份匹配时恢复。存在 pending journal、损坏、原文件与 quarantine 冲突或未知/特殊文件时 fail closed,不为腾空间冒险删除。 + +普通 `new`、`next`、`archive`、`root move` 等写命令不会自动接管陈旧锁。只有显式 `doctor --repair` 会在证明本机 owner 已不存在、锁身份未变化且没有相冲突恢复事务时接管;活动锁和无法证明陈旧的锁始终保留。 + +## 输出与退出码 + +所有命令支持 `--json`。JSON 模式只输出一个对象,包含 `command`、`exitCode`、`data`,失败时还包含结构化 `error`。 + +| 退出码 | 含义 | +| --- | --- | +| `0` | 成功 | +| `1` | 内置 `check` 完成但发现问题或结果 stale | +| `64` | 参数或用法错误 | +| `65` | 配置、状态或产物无效 | +| `73` | 锁、事务、并发 hash 或根目录冲突 | +| `75` | repair stagnation 或 hard stop 阻塞继续 | +| `70` | 未预期的内部失败 | diff --git a/.claude/skills/comet-native/reference/recovery.md b/.claude/skills/comet-native/reference/recovery.md new file mode 100644 index 0000000..5d7e552 --- /dev/null +++ b/.claude/skills/comet-native/reference/recovery.md @@ -0,0 +1,143 @@ +# Native 恢复参考 + +## 上下文恢复顺序 + +每次恢复都从磁盘事实开始: + +1. 读取项目 `.comet/config.yaml`,确认唯一 artifact root 和 `native.clarification_mode`;该字段缺失时使用 `sequential`。若有 `pending_root_move`,先运行 doctor。 +2. 运行只读的 `comet native status`;多个 active change 时读取项目级共享 `.comet/current-change.json`,确认 `workflow: native` 和目标 change,或让用户明确选择。 +3. 对目标 change 运行只读的 `show` 和 `status --details`,读取 `comet-state.yaml`、brief、拟议完整规格、verification、有界结构化 findings、`findingsTruncated` 标记和最新 checkpoint。若 findings 被截断,先处理已返回项,再重新读取 details;Verify/Archive 的验收 ID 则按 `acceptancePage.nextCursor` 独立分页取得,不依赖丢失的旧响应。 +4. 目标确认后运行 `comet native select ` 建立共享 selection;没有单独的 `resume` 命令,且只读命令不会隐式选择 change。 +5. 读取相关 canonical 规格、实现、规则、测试和当前工作区状态。 +6. 根据 phase 执行 Shape、Build、Verify 或 Archive,不依赖聊天记录猜阶段。 + +状态、Run state、trajectory 或 transaction journal 畸形时停止写入并运行只读 doctor。不要通过手工改 phase 来绕过问题。 + +## 澄清轮次恢复 + +Shape 或 Build 存在 `[blocking]` 时,从 brief 的 Open questions 恢复当前未决事项,不依赖聊天记录重建答案。Sequential 模式恢复一个最上游问题;Batch 模式按已保存编号恢复本轮全部可回答问题。切换配置不会消除已有阻塞项:先把用户答案对应回已保存问题,再按当前 `clarification_mode` 计算下一轮。 + +能从仓库、工具或运行环境查明的事实继续由 Agent 调查。宿主支持并行工作时可以并行查证,但恢复不能依赖任何可选的并行能力;调查中的事实只延后依赖它的问题。 + +Sequential 模式恢复当前最上游问题,Batch 模式恢复所有仍未回答的编号问题;两者都继续保持 `[blocking]`。全部问题解决后仍要恢复或建立最终共享理解确认;只有用户明确确认后,才能移除该阻塞项并进入 Build。该过程不增加新 phase、change 状态字段或独立决策树文件。 + +长任务在同一 phase 内中断前可写 checkpoint: + +```text +comet native checkpoint \ + --summary <已完成事实> \ + --next-action <下一动作> \ + [--artifact <项目相对路径>] \ + [--expect-revision ] +``` + +恢复时先检查 checkpoint 的 freshness。phase、revision 或 manifest 已变化时把它当历史提示,不把旧下一动作强行应用到新状态;Runtime 会在 details 中显式报告 stale 原因。 + +## 普通阶段推进 + +`next` 在 change 的 `runtime/transition.json` 先写入 prepared journal,再更新 Run state、`comet-state.yaml`、trajectory 和 checkpoint。全部完成后才删除 journal。 + +`status` 和 doctor 会报告未完成 transition。再次运行 `next` 或进入 Archive 时,runtime 会先确定性续做;也可以显式执行: + +```text +comet native doctor --repair --strategy continue +``` + +普通阶段推进没有 canonical 文件副作用,因此只支持 `continue`,不支持 `rollback`。journal 畸形时保留原文件并停止,不手工拼接状态。 + +## Baseline 缺失或不完整 + +`new` 要求 baseline 完整。Git 项目只评估 tracked 与未被 ignore 的 untracked 文件;ignored 缓存和嵌套仓库内容不会制造省略。 + +若 Runtime 返回 `baseline-snapshot-missing` 或 `baseline-snapshot-incomplete`,不要用当前文件重建 baseline,也不要把它当成 doctor 可自动修复的问题。这会丢失 change 创建以来的历史差异。 + +只能从可信备份恢复原 baseline,或保留用户编写的 brief、规格和实现事实后新建 change,重新建立完整 baseline。 + +## 证据失效与受控回退 + +进入 Verify 后,若 brief、拟议规格或项目快照变化,status 会先把失效的 implementation scope 指向受控回退;运行返回的仅含摘要的 `next` 命令回到 Build,再为变化后的 contract 取得重新确认并生成新 scope。进入 Archive 后,implementation scope、verification report 或 check receipt 等任一绑定事实变化也会触发同一回退。不要删除 finding、沿用旧 pass 或手工替换 hash ref。 + +Archive 必须使用两步预演,不能把一次旧的 ready 判断当作提交授权: + +```text +comet native archive --dry-run +comet native archive --expect-preflight <刚返回的-sha256> +``` + +第二步在锁内重算 contract、canonical base、scope、verification、当前 root 冲突与恢复状态;任何变化都会拒绝并要求重新预演。 + +## Verify fail 与修复停止 + +Verify fail 会诚实回到 Build。提交稳定、非敏感的 `--failure-category` 和 `--failed-check` 后,Runtime 先校验 token、数量与边界,再以 failure + contract + scope 形成签名:同签名第二次告警,第三次且 scope 无进展时 manual stop,单个 episode 累计到语义上限时 hard stop。 + +- implementation scope 真正变化:说明已有机械进展,普通 Build `next` 结束旧 episode 并自动开始新一轮;之前的 hard stop 不会锁死新实现。 +- scope 未变化但有一个明确新假设:只能使用 status 返回的 signature 和非空摘要做一次 `--override-repair`。 +- 同一签名已 override、或达到 hard stop:不能弱化验证或伪造 pass;保留现场并请用户决定范围、约束或是否停止。 + +一次 pass 会结束当前 repair episode;之后若旧 Archive 证据失效并重新出现相同 failure,它是新的 episode,但原 trajectory 仍保留审计事实。通用 Engine iteration budget 不参与这个产品语义。 + +## Canonical spec 冲突 + +若另一个 change 在当前 change 冻结 `base_hash` 后改变了同一 canonical spec,Archive 会停止。不要手改 hash: + +1. 重读最新 canonical spec、brief 和拟议完整规格; +2. 按用户意图改写完整目标规格,必要时先解决一个用户决定; +3. 运行 `comet native spec rebase --summary <摘要>`; +4. runtime 刷新 operation/hash,把 change 受控重开到 Build,并清除旧验证结论; +5. 重新实现、在需要时用 `--confirmed` 记录刚确认的决定、重新 Verify 和 Archive。 + +若 remove 的目标已经被并发 change 删除,rebase 会移除已满足的 remove 意图;其他 remove 会冻结最新 canonical hash 后重新验证。 + +## 当前工作区内的并行提示 + +status/Archive 会比较当前 Native root 中可见 change 的 capability、operation、base hash 和声明产物:确定冲突必须先解决;可能重叠也会在归档前阻塞。它不能看到未集成 worktree、远端分支或其他机器,因此不是分布式锁。 + +`workspace-root-changed` 和 `workspace-inspection-unavailable` 是显式 advisory,只用于解释当前 root 的事实来源,不单独阻止推进或归档。finding 会列出 `native-root-ref`、`project-root-path`、`native-root-path` 等具体漂移组件。Native 默认不读取 Git branch、HEAD 或 worktree changed paths。 + +不要把任意 `workspace-*` 都当成提示。未知 workspace 完整性 finding 仍按错误处理;Runtime 要求修复 workspace 身份时,先运行只读 doctor,再按报告执行显式 `doctor --repair`。 + +## Archive 事务 + +Archive 使用全局锁、staged specs、逐操作事件日志和备份。中断时 canonical 树可能处于事务中间状态,但 journal 会保留未完成事实。 + +Archive 的 stage、backup、apply 与 rollback copy 都通过受保护句柄读取:打开前后复核源文件、realpath、父目录身份、大小与预期 hash,目标在原子提交前再次检查。它不会因为事前路径包含检查通过,就信任后续路径仍指向同一文件。 + +write/remove 会先把 canonical 原对象身份和原内容绑定进事务,再原子改名到同目录隔离位并复验;write 使用事务私有 candidate 和无覆盖安装,rollback 也必须先隔离并验证 post 对象,再无覆盖恢复 original。即使并发替换后的内容 hash 恰好相同,只要它已经是另一个文件对象,Runtime 仍会拒绝覆盖并保留现场。 + +`events.jsonl` 读取有总字节、事件数量和单事件预算。崩溃若只留下最后一个 canonical JSON 事件的可证明前缀,Runtime 会在下一次 append 前按原始 bytes 的 hash/size 做 CAS,并原子重写到最后一个完整事件;中间坏行、完整但非法的尾行、非规范 JSON 尾部或并发改写一律 fail closed。完整但没有末尾换行的事件不会被误删,重复操作按 `type + operationId` 保持 exactly-once。 + +```text +comet native doctor +comet native doctor --repair --strategy continue +comet native doctor --repair --strategy rollback +``` + +- `continue`:从最后一个已完成操作继续,收敛到 committed archive。 +- `rollback`:按逆序恢复 canonical 文件和 active change。 +- Runtime 会先验证已移动的 archive tree、最终 state、受保护 Run、trajectory 事件和完成决定,随后才写 `archive-finalization-started`。标记写入前仍可安全 rollback;标记写入后已经跨越不可回滚边界,只能 continue,避免产生“已完成证据但又恢复为 active”的矛盾状态。 + +先阅读 doctor 的路径、transaction id 和冲突信息。若 hash 与 journal 两端都不一致,保留所有树并停止自动修复。 + +## Artifact root 迁移 + +`root move` 依次经过 `copying`、`ready`、`switched`。配置中的 `pending_root_move` 是恢复事实源;存在时普通 Native 写命令会失败关闭。 + +- `copying`:旧 root 是当前根,目标 staging 可能不完整。 +- `ready`:staging 已通过逐文件路径、大小和 SHA-256 校验,尚未切换配置。 +- `switched`:配置已指向新 root,旧 root 只有在再次验证两棵树等价、父链与目录身份后,才会先改名到事务 ID 绑定的 sibling quarantine,再复核删除。中断时 continue/rollback 会识别该 quarantine 并确定性收口,不会按旧路径直接递归删除。 + +使用 doctor 的显式 continue 或 rollback。若两棵树 hash 不一致,不删除任何一棵,并把报告中的两条路径交给用户处理。 + +## 锁与安全修复 + +doctor 区分活动锁、可证明陈旧的本机锁和无法判断的远端锁。只在 owner 进程确定不存在且没有未处理事务依赖时删除陈旧锁;不自动破坏活动或未知锁。 + +锁同时绑定 owner 元数据、锁文件身份和进程内 FIFO。普通 mutation 不会因为看到“看似过期”的时间戳就自动恢复;只有显式 doctor repair 才能执行带身份复验的 takeover,避免旧 owner 在新 owner 获锁后删除新锁而形成 split-brain。 + +doctor 可以安全清理指向不存在 change 的 selection。它不会自动重写损坏的配置、change YAML、brief、规格或 verification;这些内容必须根据用户意图人工修正后重新检查。 + +Evidence retention 遵守显式修复边界。默认 doctor 只报告候选;`--repair` 仅删除至少 30 天、每种 evidence kind 最新 32 份之外,并且依赖闭包证明未引用的 active-change 派生 evidence/receipt。 + +删除按 dependents-before-dependencies 排序,并先改名到同目录唯一 `.gc` quarantine。若在最终删除前中断,后续只读 doctor 报告 `evidence-retention-recovery-required`;显式 repair 仅在原路径不存在、quarantine 内容与身份有效时无覆盖恢复。 + +出现原文件与 quarantine 冲突、多份 quarantine、归档证据、pending 恢复、缺失依赖、损坏文档、未知目录项、symlink 或其他特殊文件时,推迟清理并失败关闭。 diff --git a/.claude/skills/comet-native/scripts/comet-native-hook-guard.mjs b/.claude/skills/comet-native/scripts/comet-native-hook-guard.mjs new file mode 100644 index 0000000..d7cc167 --- /dev/null +++ b/.claude/skills/comet-native/scripts/comet-native-hook-guard.mjs @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import { main } from './comet-native-runtime.mjs'; + +process.exitCode = await main(['hook-guard', ...process.argv.slice(2)]); diff --git a/.claude/skills/comet-native/scripts/comet-native-runtime.mjs b/.claude/skills/comet-native/scripts/comet-native-runtime.mjs new file mode 100644 index 0000000..bd78ee8 --- /dev/null +++ b/.claude/skills/comet-native/scripts/comet-native-runtime.mjs @@ -0,0 +1,29563 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path48) { + const ctrl = callVisitor(key, node, visitor, path48); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path48, ctrl); + return visit_(key, ctrl, visitor, path48); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path48 = Object.freeze(path48.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path48); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path48 = Object.freeze(path48.concat(node)); + const ck = visit_("key", node.key, visitor, path48); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path48); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path48) { + const ctrl = await callVisitor(key, node, visitor, path48); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path48, ctrl); + return visitAsync_(key, ctrl, visitor, path48); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path48 = Object.freeze(path48.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path48); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path48 = Object.freeze(path48.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path48); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path48); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path48) { + if (typeof visitor === "function") + return visitor(key, node, path48); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path48); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path48); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path48); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path48); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path48); + return void 0; + } + function replaceNode(key, path48, node) { + const parent = path48[path48.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path48, value) { + let v = value; + for (let i = path48.length - 1; i >= 0; --i) { + const k = path48[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path48) => path48 == null || typeof path48 === "object" && !!path48[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path48, value) { + if (isEmptyPath(path48)) + this.add(value); + else { + const [key, ...rest] = path48; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path48) { + const [key, ...rest] = path48; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path48, keepScalar) { + const [key, ...rest] = path48; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path48) { + const [key, ...rest] = path48; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path48, value) { + const [key, ...rest] = path48; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify4(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify4.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify4.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify4 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify4.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify5 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify5(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify4.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify4.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp2 = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp2.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp2 = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp2.intTime, + timestamp2.floatTime, + timestamp2.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp2 = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp2.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp2.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp2.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp2.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify4.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify4.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify4.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path48, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path48, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path48) { + if (Collection.isEmptyPath(path48)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path48) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path48, keepScalar) { + if (Collection.isEmptyPath(path48)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path48, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path48) { + if (Collection.isEmptyPath(path48)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path48) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path48, value) { + if (Collection.isEmptyPath(path48)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path48), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path48, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify4 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path48) => { + let item = cst; + for (const [field2, index] of path48) { + const tok = item?.[field2]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path48) => { + const parent = visit.itemAtPath(cst, path48.slice(0, -1)); + const field2 = path48[path48.length - 1][0]; + const coll = parent?.[field2]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path48, item, visitor) { + let ctrl = visitor(item, path48); + if (typeof ctrl === "symbol") + return ctrl; + for (const field2 of ["key", "value"]) { + const token = item[field2]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path48.concat([[field2, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field2 === "key") + ctrl = ctrl(item, path48); + } + } + return typeof ctrl === "function" ? ctrl(item, path48) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs34 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs34, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs34); + } else { + Object.assign(it, { key: fs34, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs34 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs34, sep: [] }); + else if (it.sep) + this.stack.push(fs34); + else + Object.assign(it, { key: fs34, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument5(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument5(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify4(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument5; + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/comet-native/native-cli-entry.ts +import { pathToFileURL } from "url"; + +// domains/comet-native/native-cli.ts +import path47 from "path"; + +// platform/fs/race-safe-read.ts +import { constants as fsConstants, promises as fs } from "fs"; + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function hasComparableFileObject(left, right) { + return hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev) && hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// platform/fs/race-safe-read.ts +var RaceSafeReadError = class extends Error { + reason; + constructor(reason, message, options) { + super(message, options); + this.name = "RaceSafeReadError"; + this.reason = reason; + } +}; +function birthtimeOf(stat) { + return "birthtimeNs" in stat && typeof stat.birthtimeNs === "bigint" ? stat.birthtimeNs : stat.birthtimeMs; +} +function ctimeOf(stat) { + return "ctimeNs" in stat && typeof stat.ctimeNs === "bigint" ? stat.ctimeNs : stat.ctimeMs; +} +function identityOf(stat) { + return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) }; +} +function sameStatIdentity(left, right) { + const leftObject = identityOf(left); + const rightObject = identityOf(right); + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && birthtimeOf(left) === birthtimeOf(right) && ctimeOf(left) === ctimeOf(right) && left.size === right.size; +} +async function readFileRaceSafe(file, maxBytes, options = {}) { + if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) { + throw new Error("race-safe read byte limit must be a positive integer"); + } + const label = options.label ?? "file"; + const bigint = options.bigint === true; + const before = await fs.lstat(file, { bigint }); + if (!before.isFile() || before.isSymbolicLink()) { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`); + } + if (BigInt(before.size) > BigInt(maxBytes)) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + const beforeRealPath = await fs.realpath(file); + await options.verify?.("pre-open", { realPath: beforeRealPath, identity: identityOf(before) }); + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + let handle; + try { + handle = await fs.open(file, flags); + } catch (error) { + if (error.code === "ELOOP") { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`, { + cause: error + }); + } + throw error; + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat({ bigint }), + fs.lstat(file, { bigint }), + fs.realpath(file) + ]); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameStatIdentity(before, opened) || !sameStatIdentity(before, pathAfterOpen)) { + throw new RaceSafeReadError("changed", `${label} changed while opening`); + } + await options.verify?.("post-open", { + realPath: realPathAfterOpen, + identity: identityOf(opened) + }); + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + for (; ; ) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat({ bigint }), + fs.lstat(file, { bigint }), + fs.realpath(file) + ]); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameStatIdentity(before, afterHandle) || !sameStatIdentity(before, afterPath)) { + throw new RaceSafeReadError("changed", `${label} changed while reading`); + } + await options.verify?.("post-read", { + realPath: afterRealPath, + identity: identityOf(afterHandle) + }); + return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-archive.ts +import { randomUUID as randomUUID6 } from "crypto"; +import { promises as fs23 } from "fs"; +import path34 from "path"; +import { isDeepStrictEqual as isDeepStrictEqual2 } from "util"; + +// domains/engine/loop.ts +import { createHash } from "crypto"; + +// domains/engine/guardrails.ts +function checkAction(action, state, guardrails, confirmations) { + if (state.iteration >= guardrails.maxIterations) { + return { allowed: false, reason: `Iteration budget exhausted: ${guardrails.maxIterations}` }; + } + if (action.type === "invoke_skill" && !guardrails.allowedSkills.includes(action.ref ?? "")) { + return { allowed: false, reason: `Skill is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.type === "call_tool" && !guardrails.allowedTools.includes(action.ref ?? "")) { + return { allowed: false, reason: `Tool is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.type === "handoff" && !guardrails.allowedAgents.includes(action.ref ?? "")) { + return { allowed: false, reason: `Agent is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.ref && guardrails.confirmationRequiredFor.includes(action.ref) && !confirmations.has(action.ref)) { + return { allowed: false, reason: `User confirmation required for: ${action.ref}` }; + } + const retries2 = state.retries[action.id] ?? 0; + if (retries2 > guardrails.maxRetriesPerAction) { + return { allowed: false, reason: `Retry budget exhausted for action: ${action.id}` }; + } + return { allowed: true }; +} + +// domains/engine/resolver.ts +function deepFreeze(value) { + if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value; + for (const key of Reflect.ownKeys(value)) { + deepFreeze(value[key]); + } + return Object.freeze(value); +} +function readonlyCopy(value) { + return deepFreeze(structuredClone(value)); +} +function resolveDeterministicStep(resolver, pkg, state, context) { + return resolver.resolveStep({ + pkg: readonlyCopy(pkg), + state: readonlyCopy(state), + context: readonlyCopy(context) + }); +} +function resolveDeterministicNext(resolver, pkg, state, step, outcome, context) { + return resolver.resolveNext({ + pkg: readonlyCopy(pkg), + state: readonlyCopy(state), + step: readonlyCopy(step), + outcome: readonlyCopy(outcome), + context: readonlyCopy(context) + }); +} + +// domains/engine/loop.ts +function actionId(runId, iteration, stepId) { + return createHash("sha256").update(`${runId}:${iteration}:${stepId ?? "adaptive"}`).digest("hex").slice(0, 16); +} +function stepFor(pkg, id) { + return pkg.definition.orchestration.steps?.find((step) => step.id === id); +} +function decideWithResolver(pkg, state, confirmations, resolver, context) { + if (state.status !== "running") return { state, action: null, reason: `Run is ${state.status}` }; + if (state.pending) + return { state, action: null, reason: `Action already pending: ${state.pending}` }; + if (state.orchestration === "adaptive") { + return { state, action: null, reason: "Adaptive orchestration requires an Agent candidate" }; + } + const resolvedStep = resolveDeterministicStep(resolver, pkg, state, context); + if (!resolvedStep && state.currentStep === null) { + return { state: { ...state, status: "completed" }, action: null }; + } + if (!resolvedStep) { + return { + state: { ...state, status: "failed" }, + action: null, + reason: `Unknown current step: ${state.currentStep}` + }; + } + const step = stepFor(pkg, resolvedStep.id); + if (!step) { + return { + state: { ...state, status: "failed" }, + action: null, + reason: `Resolver returned unknown current step: ${resolvedStep.id}` + }; + } + const resolvedState = state.currentStep === step.id ? state : { ...state, currentStep: step.id }; + const action = { + ...step.action, + id: actionId(resolvedState.runId, resolvedState.iteration, step.id), + stepId: step.id + }; + return acceptAction(pkg, resolvedState, action, confirmations); +} +function acceptAction(pkg, state, action, confirmations) { + const guard = checkAction(action, state, pkg.guardrails, confirmations); + if (!guard.allowed) return { state, action: null, reason: guard.reason }; + return { state: { ...state, pending: action.id, status: "waiting" }, action }; +} +function recordOutcomeWithResolver(pkg, state, outcome, resolver, context) { + if (!state.pending || state.pending !== outcome.actionId) { + throw new Error(`Outcome does not match pending action: ${outcome.actionId}`); + } + const resolvedStep = state.orchestration === "deterministic" ? resolveDeterministicStep(resolver, pkg, state, context) : void 0; + const step = resolvedStep ? stepFor(pkg, resolvedStep.id) : void 0; + if (state.orchestration === "deterministic" && !resolvedStep) { + throw new Error(`Unknown current step: ${state.currentStep ?? "(missing)"}`); + } + if (state.orchestration === "deterministic" && !step) { + throw new Error(`Resolver returned unknown current step: ${resolvedStep.id}`); + } + if (outcome.status === "failed") { + const retries2 = { + ...state.retries, + [outcome.actionId]: (state.retries[outcome.actionId] ?? 0) + 1 + }; + return { ...state, pending: null, status: "running", retries: retries2 }; + } + const next = state.orchestration === "deterministic" ? resolveDeterministicNext(resolver, pkg, state, step, outcome, context) : state.currentStep; + if (next !== null && state.orchestration === "deterministic" && !stepFor(pkg, next)) { + throw new Error(`Resolver returned unknown next step: ${next}`); + } + return { + ...state, + currentStep: next, + iteration: state.iteration + 1, + pending: null, + status: next === null && state.orchestration === "deterministic" ? "completed" : "running" + }; +} + +// domains/comet-native/native-artifacts.ts +import { promises as fs14 } from "fs"; +import path17 from "path"; + +// domains/comet-native/native-change.ts +var import_yaml3 = __toESM(require_dist(), 1); +import { promises as fs13 } from "fs"; +import path16 from "path"; + +// domains/comet-native/native-bounded-file.ts +import { createHash as createHash2 } from "node:crypto"; +import { promises as fs2 } from "node:fs"; +import path2 from "node:path"; +import { TextDecoder } from "node:util"; + +// domains/comet-native/native-sensitive-paths.ts +import path from "path"; +var NATIVE_EXCLUDED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([ + ".cache", + ".git", + ".gradle", + ".gnupg", + ".mypy_cache", + ".next", + ".npm", + ".pnpm-store", + ".pytest_cache", + ".ssh", + ".turbo", + ".venv", + ".yarn", + "__pycache__", + "node_modules", + "venv" +]); +var NATIVE_SENSITIVE_FILE_NAMES = /* @__PURE__ */ new Set([ + ".git-credentials", + ".netrc", + ".npmrc", + ".pypirc", + "auth.json", + "credentials.json" +]); +function isNativeEnvFileName(name) { + return name.toLowerCase().startsWith(".env"); +} +function nativeSensitiveRelativePathReason(relativeRef) { + const segments = relativeRef.replaceAll("\\", "/").split("/").filter(Boolean); + const lower = segments.map((segment) => segment.toLowerCase()); + if (lower.some((segment) => isNativeEnvFileName(segment))) return "environment-file"; + if (lower.some((segment) => NATIVE_SENSITIVE_FILE_NAMES.has(segment))) { + return "credential-config"; + } + if (lower.includes(".git")) return "git-metadata"; + if (lower.some((segment) => NATIVE_EXCLUDED_DIRECTORY_NAMES.has(segment))) { + return "dependency-or-cache"; + } + if (lower.join("/") === ".comet/config.yaml") { + return "comet-config"; + } + if (lower.join("/") === ".comet/current-change.json") { + return "comet-selection"; + } + return null; +} +function nativeSensitiveArtifactReason(paths, relativeRef) { + const generic = nativeSensitiveRelativePathReason(relativeRef); + if (generic) return generic; + const target = path.resolve(paths.projectRoot, ...relativeRef.split("/")); + const relativeNativeRoot = path.relative(paths.projectRoot, paths.nativeRoot).replaceAll("\\", "/"); + const normalized = relativeRef.replaceAll("\\", "/"); + if (normalized === relativeNativeRoot || normalized.startsWith(`${relativeNativeRoot}/`) || target === path.resolve(paths.configFile)) { + return "native-runtime"; + } + return null; +} + +// domains/comet-native/native-bounded-file.ts +var DEFAULT_NATIVE_ARTIFACT_MAX_BYTES = 1024 * 1024; +function isInside(parent, target) { + const relative = path2.relative(parent, target); + return relative === "" || !path2.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path2.sep}`); +} +function portableArtifactRef(value) { + const normalized = path2.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path2.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`Native artifact ref must be normalized and relative: ${value}`); + } + const lower = value.toLowerCase(); + const sensitiveReason = nativeSensitiveRelativePathReason(value); + if (sensitiveReason || lower === "runtime" || lower.startsWith("runtime/")) { + throw new Error( + `Native artifact ref is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${value}` + ); + } + return value; +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native artifact byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function directoryIdentity(directory) { + const stat = await fs2.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native artifact parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs2.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory) { + const lexicalRoot = path2.resolve(root); + const lexicalDirectory = path2.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error("Native artifact path is outside its root"); + } + const chain = [await directoryIdentity(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path2.relative(lexicalRoot, lexicalDirectory).split(path2.sep).filter(Boolean)) { + cursor = path2.join(cursor, segment); + const identity = await directoryIdentity(cursor); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`Native artifact parent resolves outside its root: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain(chain) { + for (const identity of chain) { + const stat = await fs2.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs2.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native artifact parent changed while reading: ${identity.path}`); + } + } +} +async function readNativeBoundedTextFile(options) { + const ref = portableArtifactRef(options.ref); + const maxBytes = positiveLimit(options.maxBytes ?? DEFAULT_NATIVE_ARTIFACT_MAX_BYTES); + const file = path2.resolve(options.root, ...ref.split("/")); + const chain = await captureDirectoryChain(options.root, path2.dirname(file)); + await options.hooks?.afterParentChainCaptured?.(); + const before = await fs2.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native artifact must be a regular file: ${ref}`); + } + if (before.size > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + const realPath = await fs2.realpath(file); + if (!isInside(chain[0].realPath, realPath)) { + throw new Error(`Native artifact resolves outside its root: ${ref}`); + } + const handle = await fs2.open(file, "r"); + try { + const [opened, afterOpenPath, afterOpenRealPath] = await Promise.all([ + handle.stat(), + fs2.lstat(file), + fs2.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!opened.isFile() || !afterOpenPath.isFile() || afterOpenPath.isSymbolicLink() || afterOpenRealPath !== realPath || !sameFileIdentity(before, opened) || !sameFileIdentity(opened, afterOpenPath)) { + throw new Error(`Native artifact changed while opening: ${ref}`); + } + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (result2.bytesRead === 0) break; + total += result2.bytesRead; + if (total > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + chunks.push(Buffer.from(buffer.subarray(0, result2.bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs2.lstat(file), + fs2.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity(opened, afterHandle) || !sameFileIdentity(opened, afterPath)) { + throw new Error(`Native artifact changed while reading: ${ref}`); + } + const bytes = Buffer.concat(chunks, total); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`Native artifact is not valid UTF-8: ${ref}`, { cause: error }); + } + return { + ref, + size: total, + hash: createHash2("sha256").update(bytes).digest("hex"), + text + }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-atomic-file.ts +import { randomUUID } from "crypto"; +import { promises as fs3 } from "fs"; +import path3 from "path"; +function isInside2(parent, target) { + const relative = path3.relative(parent, target); + return relative === "" || !path3.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path3.sep}`); +} +function sameDirectoryIdentity2(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity2(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryIdentity(directory) { + const stat = await fs3.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native atomic write parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs3.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function verifyDirectoryChain2(chain) { + for (const identity of chain) { + const stat = await fs3.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity2(identity, stat) || await fs3.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native atomic write parent changed before commit: ${identity.path}`); + } + } +} +async function prepareContainedDirectoryChain(root, directory) { + const lexicalRoot = path3.resolve(root); + const lexicalDirectory = path3.resolve(directory); + if (!isInside2(lexicalRoot, lexicalDirectory)) { + throw new Error(`Native atomic write parent is outside its managed root: ${directory}`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot)]; + const segments = path3.relative(lexicalRoot, lexicalDirectory).split(path3.sep).filter(Boolean); + let cursor = lexicalRoot; + for (const segment of segments) { + await verifyDirectoryChain2(chain); + cursor = path3.join(cursor, segment); + try { + await fs3.mkdir(cursor); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const identity = await captureDirectoryIdentity(cursor); + if (!isInside2(chain[0].realPath, identity.realPath)) { + throw new Error(`Native atomic write parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain2(chain); + return chain; +} +async function syncDirectory(directory) { + let handle; + try { + handle = await fs3.open(directory, "r"); + await handle.sync(); + } catch (error) { + const code = error.code; + if (!["EACCES", "EBADF", "EINVAL", "EISDIR", "ENOTSUP", "EPERM"].includes(code ?? "")) { + throw error; + } + } finally { + await handle?.close(); + } +} +async function atomicWrite(file, content, options = {}) { + const directory = path3.dirname(file); + const directoryChain = options.containedRoot ? await prepareContainedDirectoryChain(options.containedRoot, directory) : null; + if (!directoryChain) await fs3.mkdir(directory, { recursive: true }); + const temporary = path3.join(directory, `.${path3.basename(file)}.${randomUUID()}.tmp`); + let handle; + let temporaryIdentity; + try { + await options.beforeTemporaryOpen?.(); + handle = await fs3.open(temporary, "wx"); + temporaryIdentity = await handle.stat(); + if (directoryChain) { + const [temporaryPathStat, temporaryRealPath] = await Promise.all([ + fs3.lstat(temporary), + fs3.realpath(temporary) + ]); + await verifyDirectoryChain2(directoryChain); + if (!temporaryPathStat.isFile() || temporaryPathStat.isSymbolicLink() || !sameFileIdentity2(temporaryIdentity, temporaryPathStat) || !isInside2(directoryChain[0].realPath, temporaryRealPath)) { + throw new Error("Native atomic write temporary file opened outside its managed parent"); + } + } + if (typeof content === "string") await handle.writeFile(content, "utf8"); + else await handle.writeFile(content); + await handle.sync(); + if (!sameFileIdentity2(temporaryIdentity, await handle.stat())) { + throw new Error("Native atomic write temporary file changed while writing"); + } + await handle.close(); + handle = void 0; + await options.beforeCommit?.(); + if (directoryChain) { + await verifyDirectoryChain2(directoryChain); + const temporaryStat = await fs3.lstat(temporary); + if (!temporaryStat.isFile() || temporaryStat.isSymbolicLink() || !temporaryIdentity || !sameFileIdentity2(temporaryStat, temporaryIdentity)) { + throw new Error("Native atomic write temporary file changed before commit"); + } + } + if (options.exclusive) { + await fs3.link(temporary, file); + await fs3.unlink(temporary); + } else { + await fs3.rename(temporary, file); + } + await syncDirectory(directory); + } catch (error) { + await handle?.close(); + if (!directoryChain) { + await fs3.rm(temporary, { force: true }); + } else { + try { + await verifyDirectoryChain2(directoryChain); + await fs3.rm(temporary, { force: true }); + } catch { + } + } + throw error; + } +} +async function atomicWriteText(file, content, options = {}) { + await atomicWrite(file, content, options); +} +async function atomicWriteBytes(file, content, options = {}) { + await atomicWrite(file, content, options); +} +async function atomicWriteJson(file, value, options = {}) { + await atomicWriteText(file, JSON.stringify(value, null, 2) + "\n", options); +} + +// domains/comet-native/native-config.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { promises as fs6 } from "fs"; +import path6 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml = __toESM(require_dist(), 1); +var COMMENTS = { + en: { + schema: "# Configuration schema used by Comet. Do not edit this value.", + default_workflow: "# Default workflow entered by /comet. Must also appear in workflows.", + workflows: "# Workflows enabled in this project: native, classic, or both.", + ambient_resume: "# Enables automatic recovery through the read-only Ambient Resume probe for both Native and Classic. Set false to disable it.\n# ambient_resume: true | false", + native: "# Native workflow settings. They do not change Classic state or behavior.", + "native.artifact_root": "# Root directory where Native stores Comet specs, changes, and runtime data.", + "native.language": "# Artifact language used by Native workflow documents.\n# language: en | zh-CN", + "native.clarification_mode": "# Controls whether Native asks one clarification at a time or every currently answerable question in a round.\n# clarification_mode: sequential | batch", + "native.snapshot": "# Controls the auditable project scope and bounded work used by Native content snapshots.", + "native.snapshot.include": "# Selects the project-relative paths included in Native snapshots. Patterns use / and support *, **, and ?.", + "native.snapshot.exclude": "# Removes paths from the included scope. Exclusions are bound into each new change baseline.", + "native.snapshot.max_files": "# Bounds the number of files captured by one snapshot. Increase it for large monorepos.", + "native.snapshot.max_total_bytes": "# Bounds the total file content hashed by one snapshot. Content is streamed and does not depend on Git hashes.", + "native.snapshot.max_duration_ms": "# Bounds snapshot capture time in milliseconds. Increase it together with the byte budget on slower or larger repositories.", + classic: "# Classic workflow settings. They do not change Native state or behavior.", + "classic.language": "# Artifact language used by Classic workflow documents.\n# language: en | zh-CN", + "classic.context_compression": "# Controls beta context compression for new Classic changes.\n# context_compression: off | beta", + "classic.review_mode": "# Sets the default review depth for new Classic changes.\n# review_mode: off | standard | thorough", + "classic.auto_transition": "# Automatically enters the next Classic phase after a phase passes.\n# auto_transition: true | false" + }, + "zh-CN": { + schema: "# Comet 使用的配置格式版本,请勿修改此值。", + default_workflow: "# `/comet` 默认进入的工作流;该值也必须出现在 workflows 中。", + workflows: "# 此项目启用的工作流,可填写 native、classic 或同时启用两者。", + ambient_resume: "# 是否启用只读的环境感知恢复探针,同时作用于 Native 和 Classic;设为 false 可关闭自动工作流恢复。\n# ambient_resume: true | false", + native: "# Native 工作流配置,不会改变 Classic 的状态或行为。", + "native.artifact_root": "# Native 产物的存放根目录,包括规格、change 和运行时数据。", + "native.language": "# Native 工作流文档使用的产物语言。\n# 可选值:en | zh-CN", + "native.clarification_mode": "# Native 每轮询问一个问题,或一次提出当前所有可回答的问题。\n# 可选值:sequential | batch", + "native.snapshot": "# Native 内容快照使用的可审计项目范围与有界工作预算。", + "native.snapshot.include": "# Native 快照纳入的项目相对路径;模式使用 /,支持 *、** 和 ?。", + "native.snapshot.exclude": "# 从纳入范围中排除路径;新 change 会把排除策略绑定到 baseline。", + "native.snapshot.max_files": "# 单次快照最多捕获的文件数;大型 monorepo 可按需提高。", + "native.snapshot.max_total_bytes": "# 单次快照最多哈希的文件内容总字节数;内容采用流式读取,不依赖 Git hash。", + "native.snapshot.max_duration_ms": "# 单次快照的最长执行时间(毫秒);较慢或更大的仓库应与字节预算一并提高。", + classic: "# Classic 工作流配置,不会改变 Native 的状态或行为。", + "classic.language": "# Classic 工作流文档使用的产物语言。\n# 可选值:en | zh-CN", + "classic.context_compression": "# 新建 Classic change 是否启用 beta 上下文压缩。\n# 可选值:off | beta", + "classic.review_mode": "# 新建 Classic change 默认使用的审查深度。\n# 可选值:off | standard | thorough", + "classic.auto_transition": "# Classic 阶段通过后是否自动进入下一阶段。\n# 可选值:true | false" + } +}; +function projectConfigComment(key, language) { + return COMMENTS[language][key]; +} +function commentKey(line, block, nativeNested) { + const match = /^(\s*)([a-z_]+):/u.exec(line); + if (!match) return null; + const indent = match[1].length; + const key = match[2]; + if (indent === 0 && key in COMMENTS.en) return key; + if (indent === 2 && block) { + const blockKey = `${block}.${key}`; + if (blockKey in COMMENTS.en) return blockKey; + } + if (indent === 4 && block === "native" && nativeNested === "snapshot") { + const nestedKey = `native.snapshot.${key}`; + if (nestedKey in COMMENTS.en) return nestedKey; + } + return null; +} +function renderStructuredProjectConfig(value, language) { + const output = []; + let block = null; + let nativeNested = null; + for (const line of (0, import_yaml.stringify)(value).trimEnd().split("\n")) { + const key = commentKey(line, block, nativeNested); + if (key) { + const indent = line.match(/^\s*/u)?.[0] ?? ""; + for (const comment of projectConfigComment(key, language).split("\n")) { + output.push(`${indent}${comment}`); + } + } + output.push(line); + if (/^[a-z_]+:/u.test(line)) { + if (line.startsWith("native:")) block = "native"; + else if (line.startsWith("classic:")) block = "classic"; + else block = null; + nativeNested = null; + } else if (/^ {2}[a-z_]+:/u.test(line) && block === "native") { + nativeNested = line.startsWith(" snapshot:") ? "snapshot" : null; + } + } + output.push(""); + return output.join("\n"); +} + +// domains/comet-native/native-protected-file.ts +import { createHash as createHash3 } from "node:crypto"; +import { constants as fsConstants2, promises as fs4 } from "node:fs"; +import path4 from "node:path"; +import { TextDecoder as TextDecoder2 } from "node:util"; +function isInside3(parent, target) { + const relative = path4.relative(parent, target); + return relative === "" || !path4.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path4.sep}`); +} +function positiveLimit2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity3(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity3(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity2(directory, label) { + const stat = await fs4.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs4.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain2(root, directory, label) { + const lexicalRoot = path4.resolve(root); + const lexicalDirectory = path4.resolve(directory); + if (!isInside3(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity2(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path4.relative(lexicalRoot, lexicalDirectory).split(path4.sep).filter(Boolean)) { + await verifyDirectoryChain3(chain, label); + cursor = path4.join(cursor, segment); + const identity = await captureDirectoryIdentity2(cursor, label); + if (!isInside3(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain3(chain, label); + return chain; +} +async function verifyDirectoryChain3(chain, label) { + for (const identity of chain) { + const stat = await fs4.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity3(identity, stat) || await fs4.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit2(options.maxBytes); + const file = path4.resolve(options.file); + const chain = await captureDirectoryChain2(options.root, path4.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity2(path4.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain3(chain, options.label); + const before = await fs4.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs4.realpath(file); + if (!isInside3(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside3(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants2.O_RDONLY : fsConstants2.O_RDONLY | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK; + const handle = await fs4.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs4.lstat(file), + fs4.realpath(file) + ]); + await verifyDirectoryChain3(chain, options.label); + await verifyDirectoryChain3(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity3(beforeIdentity, opened) || !sameFileIdentity3(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs4.lstat(file), + fs4.realpath(file) + ]); + await verifyDirectoryChain3(chain, options.label); + await verifyDirectoryChain3(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity3(beforeIdentity, afterHandle) || !sameFileIdentity3(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash3("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot2 = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder2("utf-8", { fatal: true }).decode(snapshot2.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot2, text }; +} +async function readNativeProtectedDirectory(options) { + const chain = await captureDirectoryChain2(options.root, options.directory, options.label); + let entries; + if (options.maxEntries === void 0) { + entries = await fs4.readdir(options.directory, { withFileTypes: true }); + } else { + const maxEntries = positiveLimit2(options.maxEntries); + entries = []; + const directory = await fs4.opendir(options.directory); + try { + for await (const entry2 of directory) { + entries.push(entry2); + if (entries.length > maxEntries) { + throw new Error(`${options.label} exceeds ${maxEntries} entries`); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + } + await verifyDirectoryChain3(chain, options.label); + return { + entries, + verify: () => verifyDirectoryChain3(chain, options.label) + }; +} +async function captureNativeProtectedDirectoryGuard(options) { + const chain = await captureDirectoryChain2(options.root, options.directory, options.label); + return { verify: () => verifyDirectoryChain3(chain, options.label) }; +} +async function quarantineNativeProtectedDirectoryInternal(options) { + const directory = path4.resolve(options.directory); + const quarantine = path4.resolve(options.quarantine); + if (path4.dirname(quarantine) !== path4.dirname(directory) || !isInside3(path4.resolve(options.root), quarantine) || quarantine === directory) { + throw new Error(`${options.label} quarantine must be a distinct sibling inside its root`); + } + const parentChain = await captureDirectoryChain2( + options.root, + path4.dirname(directory), + options.label + ); + const identity = await captureDirectoryIdentity2(directory, options.label); + if (!isInside3(parentChain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + await options.beforeQuarantine?.(); + await verifyDirectoryChain3(parentChain, options.label); + const current = await fs4.lstat(directory); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(identity, current) || await fs4.realpath(directory) !== identity.realPath) { + throw new Error(`${options.label} changed before quarantine`); + } + try { + await fs4.lstat(quarantine); + throw new Error(`${options.label} quarantine path is occupied`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await fs4.rename(directory, quarantine); + await verifyDirectoryChain3(parentChain, options.label); + const quarantined = await fs4.lstat(quarantine); + if (!quarantined.isDirectory() || quarantined.isSymbolicLink() || !sameDirectoryIdentity3(identity, quarantined)) { + throw new Error(`${options.label} changed while quarantining`); + } + await options.afterQuarantine?.(quarantine); + try { + await fs4.lstat(directory); + throw new Error(`${options.label} path was recreated while quarantining`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + return { quarantine, parentChain, identity }; +} +async function quarantineNativeProtectedDirectory(options) { + await quarantineNativeProtectedDirectoryInternal(options); +} +async function removeNativeProtectedFile(options) { + const file = path4.resolve(options.file); + const parentChain = await captureDirectoryChain2(options.root, path4.dirname(file), options.label); + const before = await fs4.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + const identity = asFileIdentity(before); + const realPath = await fs4.realpath(file); + if (!isInside3(parentChain[0].realPath, realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + const snapshot2 = await readNativeProtectedFile({ + root: options.root, + file, + maxBytes: options.maxBytes, + label: options.label + }); + if (snapshot2.hash !== options.expectedHash || snapshot2.size !== options.expectedSize) { + throw new Error(`${options.label} changed before removal`); + } + const [afterRead, afterReadRealPath] = await Promise.all([fs4.lstat(file), fs4.realpath(file)]); + if (!afterRead.isFile() || afterRead.isSymbolicLink() || !sameFileIdentity3(identity, afterRead) || afterReadRealPath !== realPath) { + throw new Error(`${options.label} changed while verifying removal`); + } + await options.beforeRemove?.(); + await verifyDirectoryChain3(parentChain, options.label); + const [current, currentRealPath] = await Promise.all([fs4.lstat(file), fs4.realpath(file)]); + if (!current.isFile() || current.isSymbolicLink() || !sameFileIdentity3(identity, current) || currentRealPath !== realPath) { + throw new Error(`${options.label} changed before removal`); + } + await fs4.rm(file); + await verifyDirectoryChain3(parentChain, options.label); +} +async function removeNativeProtectedEmptyDirectory(options) { + const directory = path4.resolve(options.directory); + const parentChain = await captureDirectoryChain2( + options.root, + path4.dirname(directory), + options.label + ); + const identity = await captureDirectoryIdentity2(directory, options.label); + if (!isInside3(parentChain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + await options.beforeRemove?.(); + await verifyDirectoryChain3(parentChain, options.label); + const current = await fs4.lstat(directory); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(identity, current) || await fs4.realpath(directory) !== identity.realPath) { + throw new Error(`${options.label} changed before removal`); + } + await fs4.rmdir(directory); + await verifyDirectoryChain3(parentChain, options.label); +} +async function ensureNativeProtectedDirectory(options) { + const lexicalRoot = path4.resolve(options.root); + const lexicalDirectory = path4.resolve(options.directory); + if (!isInside3(lexicalRoot, lexicalDirectory)) { + throw new Error(`${options.label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity2(lexicalRoot, options.label)]; + let cursor = lexicalRoot; + for (const segment of path4.relative(lexicalRoot, lexicalDirectory).split(path4.sep).filter(Boolean)) { + await verifyDirectoryChain3(chain, options.label); + cursor = path4.join(cursor, segment); + try { + await fs4.mkdir(cursor); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const identity = await captureDirectoryIdentity2(cursor, options.label); + if (!isInside3(chain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain3(chain, options.label); +} +async function moveNativeProtectedDirectory(options) { + const root = path4.resolve(options.root); + const source = path4.resolve(options.source); + const target = path4.resolve(options.target); + if (source === target || !isInside3(root, source) || !isInside3(root, target) || isInside3(source, target) || isInside3(target, source)) { + throw new Error(`${options.label} source and target must be distinct paths inside one root`); + } + await ensureNativeProtectedDirectory({ + root, + directory: path4.dirname(target), + label: `${options.label} target parent` + }); + const sourceParentChain = await captureDirectoryChain2(root, path4.dirname(source), options.label); + const targetParentChain = await captureDirectoryChain2(root, path4.dirname(target), options.label); + const sourceIdentity = await captureDirectoryIdentity2(source, options.label); + if (!isInside3(sourceParentChain[0].realPath, sourceIdentity.realPath)) { + throw new Error(`${options.label} source resolves outside its managed root`); + } + try { + await fs4.lstat(target); + throw new Error(`${options.label} target already exists`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await options.beforeMove?.(); + await Promise.all([ + verifyDirectoryChain3(sourceParentChain, options.label), + verifyDirectoryChain3(targetParentChain, options.label) + ]); + const current = await fs4.lstat(source); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(sourceIdentity, current) || await fs4.realpath(source) !== sourceIdentity.realPath) { + throw new Error(`${options.label} source changed before move`); + } + await fs4.rename(source, target); + await Promise.all([ + verifyDirectoryChain3(sourceParentChain, options.label), + verifyDirectoryChain3(targetParentChain, options.label) + ]); + const moved = await fs4.lstat(target); + if (!moved.isDirectory() || moved.isSymbolicLink() || !sameDirectoryIdentity3(sourceIdentity, moved) || !isInside3(sourceParentChain[0].realPath, await fs4.realpath(target))) { + throw new Error(`${options.label} source identity changed while moving`); + } + try { + await fs4.lstat(source); + throw new Error(`${options.label} source was recreated while moving`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } +} +async function copyNativeProtectedFile(options) { + const source = await readNativeProtectedFile({ + root: options.sourceRoot, + file: options.source, + maxBytes: options.maxBytes, + label: options.label, + hooks: options.hooks, + forbiddenRoots: options.forbiddenRoots + }); + if (options.expectedHash !== void 0 && source.hash !== options.expectedHash) { + throw new Error(`${options.label} content changed before copy`); + } + await atomicWriteBytes(options.target, source.bytes, { + containedRoot: options.targetRoot, + exclusive: options.exclusive, + beforeCommit: async () => { + await options.hooks?.beforeTargetCommit?.(); + if (options.expectedTargetHash === void 0) return; + if (options.expectedTargetHash === null) { + try { + await fs4.lstat(options.target); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + throw new Error(`${options.label} target changed before commit`); + } + const target = await readNativeProtectedFile({ + root: options.targetRoot, + file: options.target, + maxBytes: options.maxBytes, + label: `${options.label} existing target` + }); + if (target.hash !== options.expectedTargetHash) { + throw new Error(`${options.label} target changed before commit`); + } + } + }); + const persisted = await readNativeProtectedFile({ + root: options.targetRoot, + file: options.target, + maxBytes: options.maxBytes, + label: `${options.label} target` + }); + if (persisted.hash !== source.hash || persisted.size !== source.size) { + throw new Error(`${options.label} target could not be verified`); + } + return persisted; +} + +// domains/comet-native/native-paths.ts +import { promises as fs5 } from "fs"; +import path5 from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs5.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs5.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +function inside(parent, target) { + const relative = path5.relative(parent, target); + return relative === "" || !path5.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path5.sep}`); +} +async function physicalPath(target) { + const missing = []; + let cursor = target; + while (!await isFileOrDirectory(cursor)) { + const parent = path5.dirname(cursor); + if (parent === cursor) break; + missing.push(path5.basename(cursor)); + cursor = parent; + } + const existing = await fs5.realpath(cursor); + return path5.resolve(existing, ...missing.reverse()); +} +async function isSymbolicLink(target) { + try { + return (await fs5.lstat(target)).isSymbolicLink(); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path5.resolve(startPath); + try { + if (!(await fs5.stat(cursor)).isDirectory()) cursor = path5.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path5.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path5.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path5.join(cursor, ".git"))) return cursor; + const parent = path5.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path5.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized = path5.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized === ".." || normalized.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized === "" ? "." : normalized; +} +async function resolveArtifactRoot(projectRoot, value) { + const normalized = normalizeArtifactRootRef(value); + const lexical = path5.resolve(projectRoot, ...normalized.split("/")); + const physicalProject = await fs5.realpath(projectRoot); + const physicalTarget = await physicalPath(lexical); + if (!inside(physicalProject, physicalTarget)) { + throw new Error("native.artifact_root resolves outside the project root"); + } + return lexical; +} +async function nativeProjectPaths(projectRoot, artifactRootRef) { + const normalized = normalizeArtifactRootRef(artifactRootRef); + const artifactRoot = await resolveArtifactRoot(projectRoot, normalized); + const nativeRoot = path5.join(artifactRoot, "comet"); + if (await isSymbolicLink(nativeRoot)) { + throw new Error("The configured Native comet root must not be a symbolic link"); + } + const [physicalArtifactRoot, physicalNativeRoot] = await Promise.all([ + physicalPath(artifactRoot), + physicalPath(nativeRoot) + ]); + if (!inside(physicalArtifactRoot, physicalNativeRoot)) { + throw new Error("The configured Native comet root resolves outside its artifact root"); + } + return { + projectRoot: path5.resolve(projectRoot), + configFile: path5.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")), + artifactRoot, + artifactRootRef: normalized, + nativeRoot, + specsDir: path5.join(nativeRoot, "specs"), + changesDir: path5.join(nativeRoot, "changes"), + archiveDir: path5.join(nativeRoot, "archive"), + runtimeDir: path5.join(nativeRoot, "runtime"), + locksDir: path5.join(nativeRoot, "runtime", "locks"), + transactionsDir: path5.join(nativeRoot, "runtime", "transactions") + }; +} +async function ensureNativeDirectories(paths) { + const directories = [ + paths.specsDir, + paths.changesDir, + paths.archiveDir, + paths.locksDir, + paths.transactionsDir + ]; + await Promise.all( + directories.map(async (directory) => { + await resolveContainedNativePath(paths.nativeRoot, directory); + await fs5.mkdir(directory, { recursive: true }); + }) + ); +} +function isInsidePath(parent, target) { + return inside(path5.resolve(parent), path5.resolve(target)); +} +async function resolveContainedNativePath(root, target) { + const lexicalRoot = path5.resolve(root); + const lexicalTarget = path5.resolve(target); + if (!inside(lexicalRoot, lexicalTarget)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + if (await isSymbolicLink(lexicalRoot)) { + throw new Error(`Native root must not be a symbolic link: ${root}`); + } + const [physicalRoot, physicalTarget] = await Promise.all([ + physicalPath(lexicalRoot), + physicalPath(lexicalTarget) + ]); + if (!inside(physicalRoot, physicalTarget)) { + throw new Error(`Path resolves outside the Native root: ${target}`); + } + return lexicalTarget; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot2 = record(value, "native.snapshot"); + rejectUnknown(snapshot2, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot2.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot2.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot2.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot2.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot2.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot2 = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot: snapshot2, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +function defaultProjectConfig(artifactRoot = "docs", language = "en") { + return { + schema: "comet.project.v1", + default_workflow: "native", + ambient_resume: true, + native: { + artifact_root: normalizeArtifactRootRef(artifactRoot), + language, + clarification_mode: "sequential", + snapshot: { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] } + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs6.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} +async function existingConfigDocument(projectRoot) { + const file = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + try { + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) throw new Error(document.errors[0].message); + const value = document.toJS(); + if (value && typeof value === "object" && !Array.isArray(value)) { + return value; + } + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + return {}; +} +async function assertNoPendingNativeRootMove(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (config?.native.pending_root_move) { + throw new Error( + `Native root move ${config.native.pending_root_move.id} is incomplete; use comet native doctor --repair` + ); + } +} +async function writeProjectConfig(projectRoot, config) { + const validated = parseConfig({ + schema: config.schema, + default_workflow: config.default_workflow, + workflows: config.workflows ?? [config.default_workflow], + ambient_resume: config.ambient_resume, + native: { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot, + ...config.native.pending_root_move ? { + pending_root_move: { + id: config.native.pending_root_move.id, + from_artifact_root: config.native.pending_root_move.fromArtifactRoot, + to_artifact_root: config.native.pending_root_move.toArtifactRoot, + stage: config.native.pending_root_move.stage, + ...config.native.pending_root_move.cleanup ? { + cleanup: { + kind: config.native.pending_root_move.cleanup.kind, + state: config.native.pending_root_move.cleanup.state, + manifest_hash: config.native.pending_root_move.cleanup.manifestHash + } + } : {} + } + } : {} + } + }); + const document = { + ...await existingConfigDocument(projectRoot), + schema: validated.schema, + default_workflow: validated.default_workflow, + workflows: validated.workflows, + ambient_resume: validated.ambient_resume, + native: { + artifact_root: validated.native.artifact_root, + language: validated.native.language, + clarification_mode: validated.native.clarification_mode, + snapshot: validated.native.snapshot, + ...validated.native.pending_root_move ? { + pending_root_move: { + id: validated.native.pending_root_move.id, + from_artifact_root: validated.native.pending_root_move.fromArtifactRoot, + to_artifact_root: validated.native.pending_root_move.toArtifactRoot, + stage: validated.native.pending_root_move.stage, + ...validated.native.pending_root_move.cleanup ? { + cleanup: { + kind: validated.native.pending_root_move.cleanup.kind, + state: validated.native.pending_root_move.cleanup.state, + manifest_hash: validated.native.pending_root_move.cleanup.manifestHash + } + } : {} + } + } : {} + } + }; + const canonical = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + await fs6.mkdir(path6.dirname(canonical), { recursive: true }); + await atomicWriteText( + canonical, + renderStructuredProjectConfig(document, validated.native.language === "zh-CN" ? "zh-CN" : "en") + ); +} +async function resolveNativeProject(options) { + const projectRoot = await discoverNativeProject(options.startPath); + const existing = await readProjectConfig(projectRoot); + if (!existing && options.allowMissingConfig === false) { + throw new Error(`${PROJECT_CONFIG_FILE} was not found`); + } + if (existing?.native.pending_root_move) { + throw new Error( + `Native root move ${existing.native.pending_root_move.id} is incomplete; use comet native doctor --repair` + ); + } + const explicit = options.explicitArtifactRoot ? normalizeArtifactRootRef(options.explicitArtifactRoot) : void 0; + if (existing && explicit && explicit !== existing.native.artifact_root) { + throw new Error( + `Configured Native artifact root is ${existing.native.artifact_root}; refusing conflicting root ${explicit}` + ); + } + const config = existing ?? defaultProjectConfig(explicit ?? "docs"); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + return { config, paths, configured: existing !== null }; +} + +// domains/comet-native/native-mutation-lock.ts +import { promises as fs9 } from "fs"; +import path9 from "path"; + +// domains/comet-native/native-lock.ts +import { AsyncLocalStorage } from "async_hooks"; +import { randomUUID as randomUUID2 } from "crypto"; +import { promises as fs7 } from "fs"; +import os2 from "os"; +import path7 from "path"; +var NATIVE_LOCK_MAX_BYTES = 16 * 1024; +var NATIVE_LOCK_COORDINATOR_DIR = ".coordinator"; +var NATIVE_LOCK_COORDINATOR_TIMEOUT_MS = 5e3; +var nativeLockCoordinator = new AsyncLocalStorage(); +var nativeLockLocalCoordinator = /* @__PURE__ */ new Map(); +function lockName(value) { + if (!/^[a-z][a-z0-9-]*$/u.test(value)) throw new Error(`Invalid Native lock name: ${value}`); + return `${value}.lock`; +} +function parseNativeLockOwner(value, file) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Invalid Native lock metadata: ${file}`); + } + const owner = value; + if (typeof owner.id !== "string" || owner.id.length === 0 || typeof owner.pid !== "number" || !Number.isSafeInteger(owner.pid) || owner.pid < 1 || typeof owner.hostname !== "string" || owner.hostname.length === 0 || typeof owner.createdAt !== "string" || owner.createdAt.length === 0 || typeof owner.operation !== "string" || owner.operation.length === 0) { + throw new Error(`Invalid Native lock metadata: ${file}`); + } + return owner; +} +function nativeLockFileIdentity(stat) { + return { + device: stat.dev.toString(), + inode: stat.ino.toString(), + size: stat.size.toString(), + birthtimeNs: stat.birthtimeNs.toString(), + ctimeNs: stat.ctimeNs.toString(), + mtimeNs: stat.mtimeNs.toString() + }; +} +function sameNativeLockObject(left, right) { + const leftObject = { dev: left.device, ino: left.inode, birthtime: left.birthtimeNs }; + const rightObject = { dev: right.device, ino: right.inode, birthtime: right.birthtimeNs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.size === right.size; +} +function sameNativeLockVersion(left, right) { + return sameNativeLockObject(left, right) && left.size === right.size && left.birthtimeNs === right.birthtimeNs && left.ctimeNs === right.ctimeNs && left.mtimeNs === right.mtimeNs; +} +function sameNativeLockDiagnosis(left, right) { + if (left.status !== right.status) return false; + if (!left.owner || !left.identity || !right.owner || !right.identity) { + return left.owner === right.owner && left.identity === right.identity; + } + return left.owner.id === right.owner.id && sameNativeLockVersion(left.identity, right.identity); +} +async function readNativeLockSnapshot(file) { + let bytes; + let stat; + try { + const result2 = await readFileRaceSafe(file, NATIVE_LOCK_MAX_BYTES, { + bigint: true, + label: "Native lock" + }); + bytes = result2.bytes; + stat = result2.stat; + } catch (error) { + if (error.code === "ENOENT") return null; + if (error instanceof RaceSafeReadError) { + if (error.reason === "too-large") { + throw new Error(`Native lock metadata exceeds ${NATIVE_LOCK_MAX_BYTES} bytes: ${file}`, { + cause: error + }); + } + if (error.reason === "not-regular-file") { + throw new Error(`Native lock must be a regular file: ${file}`, { cause: error }); + } + throw new Error(`Native lock changed while reading: ${file}`, { cause: error }); + } + throw error; + } + return { + file, + owner: parseNativeLockOwner(JSON.parse(bytes.toString("utf8")), file), + identity: nativeLockFileIdentity(stat) + }; +} +async function readNativeLock(file) { + return (await readNativeLockSnapshot(file))?.owner ?? null; +} +function diagnosisFromSnapshot(snapshot2) { + if (!snapshot2) return { status: "missing", owner: null, identity: null }; + if (snapshot2.owner.hostname !== os2.hostname()) { + return { status: "unknown", owner: snapshot2.owner, identity: snapshot2.identity }; + } + const alive = isProcessAlive(snapshot2.owner.pid); + return { + status: alive === true ? "active" : alive === false ? "stale" : "unknown", + owner: snapshot2.owner, + identity: snapshot2.identity + }; +} +async function restoreQuarantinedNativeLock(quarantine, file) { + try { + await fs7.lstat(file); + return; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + try { + await fs7.rename(quarantine, file); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } +} +async function removeBoundNativeLock(expected, quarantineDir) { + const current = await readNativeLockSnapshot(expected.file); + if (!current) return "missing"; + if (current.owner.id !== expected.owner.id) { + throw new Error(`Native lock ownership changed: ${expected.file}`); + } + if (!sameNativeLockVersion(current.identity, expected.identity)) { + throw new Error(`Native lock identity changed: ${expected.file}`); + } + await fs7.mkdir(quarantineDir, { recursive: true }); + const quarantine = path7.join( + quarantineDir, + `${path7.basename(expected.file)}.${expected.owner.id}.${randomUUID2()}.removed` + ); + try { + await fs7.rename(expected.file, quarantine); + } catch (error) { + if (error.code === "ENOENT") return "missing"; + throw error; + } + const moved = await readNativeLockSnapshot(quarantine); + if (!moved || moved.owner.id !== expected.owner.id || !sameNativeLockObject(moved.identity, expected.identity)) { + await restoreQuarantinedNativeLock(quarantine, expected.file); + throw new Error(`Native lock changed before quarantine: ${expected.file}`); + } + await fs7.rm(quarantine, { force: true }); + return "removed"; +} +function newNativeLockOwner(operation) { + return { + id: randomUUID2(), + pid: process.pid, + hostname: os2.hostname(), + createdAt: (/* @__PURE__ */ new Date()).toISOString(), + operation + }; +} +async function writeNativeLockFile(file, owner) { + let handle; + try { + handle = await fs7.open(file, "wx"); + } catch (error) { + if (error.code === "EEXIST") { + const existing = await readNativeLock(file); + throw new Error( + `Native lock is already held: ${file}${existing ? ` by pid ${existing.pid} for ${existing.operation}` : ""}`, + { cause: error } + ); + } + throw error; + } + try { + await handle.writeFile(JSON.stringify(owner, null, 2) + "\n", "utf8"); + await handle.sync(); + return nativeLockFileIdentity(await handle.stat({ bigint: true })); + } finally { + await handle.close(); + } +} +async function publishNativeCoordinatorClaim(paths, operation) { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + await fs7.mkdir(locksDir, { recursive: true }); + const coordinatorDir = await resolveContainedNativePath( + paths.nativeRoot, + path7.join(locksDir, NATIVE_LOCK_COORDINATOR_DIR) + ); + await fs7.mkdir(coordinatorDir, { recursive: true }); + const owner = newNativeLockOwner(operation); + const temporary = path7.join(coordinatorDir, `.${owner.id}.tmp`); + const file = path7.join(coordinatorDir, `${owner.id}.claim`); + try { + const identity = await writeNativeLockFile(temporary, owner); + await fs7.rename(temporary, file); + const published = await readNativeLockSnapshot(file); + if (!published || !sameNativeLockObject(identity, published.identity)) { + throw new Error(`Native lock coordinator claim changed while publishing: ${file}`); + } + return { file, nativeRoot: paths.nativeRoot, locksDir, owner, identity: published.identity }; + } finally { + await fs7.rm(temporary, { force: true }); + } +} +async function hasNativeCoordinatorPredecessor(claim) { + const coordinatorDir = path7.dirname(claim.file); + let predecessor = false; + const claimName = path7.basename(claim.file); + for (const entry2 of await fs7.readdir(coordinatorDir, { withFileTypes: true })) { + if (!entry2.isFile() || entry2.isSymbolicLink() || !entry2.name.endsWith(".claim")) continue; + const file = path7.join(coordinatorDir, entry2.name); + if (path7.resolve(file) === path7.resolve(claim.file)) continue; + try { + const snapshot2 = await readNativeLockSnapshot(file); + const diagnosis = diagnosisFromSnapshot(snapshot2); + if (diagnosis.status === "missing") continue; + if (diagnosis.status === "stale" && snapshot2) { + await removeBoundNativeLock(snapshot2, coordinatorDir); + continue; + } + if (entry2.name < claimName) predecessor = true; + } catch { + if (entry2.name < claimName) predecessor = true; + } + } + return predecessor; +} +async function releaseNativeCoordinatorClaim(claim) { + const current = await readNativeLockSnapshot(claim.file); + if (!current) return; + if (current.owner.id !== claim.owner.id || !sameNativeLockVersion(current.identity, claim.identity)) { + throw new Error(`Native lock coordinator ownership changed: ${claim.file}`); + } + await removeBoundNativeLock(current, path7.dirname(claim.file)); +} +async function acquireNativeCoordinator(paths, operation) { + const deadline = Date.now() + NATIVE_LOCK_COORDINATOR_TIMEOUT_MS; + while (true) { + const claim = await publishNativeCoordinatorClaim(paths, operation); + if (!await hasNativeCoordinatorPredecessor(claim)) return claim; + await releaseNativeCoordinatorClaim(claim); + if (Date.now() >= deadline) { + throw new Error(`Native lock coordinator is busy: ${paths.locksDir}`); + } + await new Promise((resolve) => setTimeout(resolve, 2 + Math.floor(Math.random() * 7))); + } +} +async function acquireNativeLocalCoordinator(key) { + const previous = nativeLockLocalCoordinator.get(key) ?? Promise.resolve(); + let release; + const current = new Promise((resolve) => { + release = resolve; + }); + const turn = previous.then(() => current); + nativeLockLocalCoordinator.set(key, turn); + await previous; + return () => { + release(); + if (nativeLockLocalCoordinator.get(key) === turn) nativeLockLocalCoordinator.delete(key); + }; +} +async function withNativeLockCoordinator(paths, operation, work) { + const key = path7.resolve(paths.locksDir); + const current = nativeLockCoordinator.getStore(); + if (current?.has(key)) return work(); + const releaseLocal = await acquireNativeLocalCoordinator(key); + try { + const claim = await acquireNativeCoordinator(paths, operation); + const next = new Map(current ?? []); + next.set(key, claim); + return await nativeLockCoordinator.run(next, async () => { + try { + return await work(); + } finally { + await releaseNativeCoordinatorClaim(claim); + } + }); + } finally { + releaseLocal(); + } +} +async function withNativeLockRecovery(pathEntries, operation, work) { + const unique = [ + ...new Map(pathEntries.map((entry2) => [path7.resolve(entry2.locksDir), entry2])).values() + ].sort((left, right) => path7.resolve(left.locksDir).localeCompare(path7.resolve(right.locksDir))); + const enter = async (index) => { + const entry2 = unique[index]; + return entry2 ? withNativeLockCoordinator(entry2, operation, () => enter(index + 1)) : work(); + }; + return enter(0); +} +async function acquireNativeLock(paths, name, operation) { + return withNativeLockCoordinator(paths, `acquire ${name}`, async () => { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + await fs7.mkdir(locksDir, { recursive: true }); + const file = await resolveContainedNativePath( + paths.nativeRoot, + path7.join(locksDir, lockName(name)) + ); + const owner = newNativeLockOwner(operation); + const identity = await writeNativeLockFile(file, owner); + return { file, nativeRoot: paths.nativeRoot, locksDir, owner, identity }; + }); +} +async function releaseNativeLock(lock) { + if (!await readNativeLockSnapshot(lock.file)) return; + await withNativeLockCoordinator(lock, `release ${path7.basename(lock.file)}`, async () => { + const current = await readNativeLockSnapshot(lock.file); + if (!current) return; + if (current.owner.id !== lock.owner.id) { + throw new Error(`Native lock ownership changed: ${lock.file}`); + } + if (!sameNativeLockVersion(current.identity, lock.identity)) { + throw new Error(`Native lock identity changed: ${lock.file}`); + } + const coordinatorDir = path7.join(lock.locksDir, NATIVE_LOCK_COORDINATOR_DIR); + await removeBoundNativeLock(current, coordinatorDir); + }); +} +function isProcessAlive(pid) { + try { + process.kill(pid, 0); + return true; + } catch (error) { + const code = error.code; + if (code === "ESRCH") return false; + if (code === "EPERM") return true; + return null; + } +} +async function diagnoseNativeLock(file) { + return diagnosisFromSnapshot(await readNativeLockSnapshot(file)); +} +async function takeOverNativeStaleLock(paths, file, expected) { + return withNativeLockCoordinator(paths, `take over ${path7.basename(file)}`, async () => { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + const containedFile = await resolveContainedNativePath(paths.nativeRoot, file); + if (path7.resolve(path7.dirname(containedFile)) !== path7.resolve(locksDir)) { + throw new Error(`Native lock takeover target is outside the lock directory: ${file}`); + } + const snapshot2 = await readNativeLockSnapshot(containedFile); + const diagnosis = diagnosisFromSnapshot(snapshot2); + if (diagnosis.status === "missing") return { status: "missing" }; + if (expected && !sameNativeLockDiagnosis(expected, diagnosis)) { + return { status: "changed", diagnosis }; + } + if (diagnosis.status !== "stale" || !snapshot2) { + return { status: "changed", diagnosis }; + } + const coordinatorDir = path7.join(locksDir, NATIVE_LOCK_COORDINATOR_DIR); + const removed = await removeBoundNativeLock(snapshot2, coordinatorDir); + return removed === "removed" ? { status: "removed", owner: snapshot2.owner } : { status: "missing" }; + }); +} + +// domains/comet-native/native-transaction.ts +import { promises as fs8 } from "fs"; +import path8 from "path"; +import { TextDecoder as TextDecoder3 } from "util"; +var JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "kind", + "status", + "projectRoot", + "nativeRoot", + "change", + "createdAt", + "operations" +]); +var OPERATION_KEYS = /* @__PURE__ */ new Set(["id", "type", "source", "target", "staged", "backup"]); +var ARCHIVE_V2_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "kind", + "status", + "change", + "createdAt", + "preflightHash", + "operations" +]); +var ARCHIVE_V2_OPERATION_KEYS = /* @__PURE__ */ new Set([ + "id", + "type", + "source", + "target", + "staged", + "backup", + "expectedSourceHash", + "expectedTargetHash", + "stagedHash" +]); +var EVENT_KEYS = /* @__PURE__ */ new Set(["sequence", "timestamp", "type", "operationId"]); +var TRANSACTION_STATUSES = /* @__PURE__ */ new Set([ + "prepared", + "applying", + "committed", + "rolling-back", + "rolled-back" +]); +var EVENT_TYPES = /* @__PURE__ */ new Set([ + "prepared", + "operation-started", + "operation-completed", + "archive-finalization-started", + "archive-finalized", + "commit", + "rollback-started", + "rollback-completed" +]); +var NATIVE_TRANSACTION_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_TRANSACTION_EVENTS_MAX_BYTES = 1024 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_BYTES = 16 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_COUNT = 1024; +var NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES = 64 * 1024 * 1024; +var NATIVE_LEGACY_TRANSACTION_DIRECTORY_MAX_ENTRIES = 2e4; +var UTF8_DECODER = new TextDecoder3("utf-8", { fatal: true }); +function record2(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function rejectUnknown2(value, keys, label) { + const unknown = Object.keys(value).filter((key) => !keys.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function validTimestamp(value) { + if (typeof value !== "string") return false; + const parsed = new Date(value); + return !Number.isNaN(parsed.valueOf()) && parsed.toISOString() === value; +} +function assertRef(ref, label) { + if (typeof ref !== "string" || ref.length === 0 || path8.isAbsolute(ref) || /^(?:[A-Za-z]:|~|[\\/])/u.test(ref) || ref.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native root`); + } +} +function assertHash(value, label) { + if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } +} +function assertArchiveRef(ref, label) { + assertRef(ref, label); + if (ref.includes("\\") || ref !== path8.posix.normalize(ref) || ref.split("/").includes(".") || ref.endsWith("/") || Buffer.byteLength(ref, "utf8") > 1024) { + throw new Error(`${label} must be a normalized Native-relative ref`); + } +} +function parseOperation(value, index) { + const operation = record2(value, `transaction operations[${index}]`); + rejectUnknown2(operation, OPERATION_KEYS, `transaction operations[${index}]`); + if (typeof operation.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(operation.id)) { + throw new Error(`transaction operations[${index}].id is invalid`); + } + if (operation.type !== "write" && operation.type !== "remove" && operation.type !== "move") { + throw new Error(`transaction operation ${operation.id} has an invalid type`); + } + assertRef(operation.target, `transaction operation ${operation.id} target`); + for (const field2 of ["source", "staged", "backup"]) { + if (operation[field2] !== void 0) { + assertRef(operation[field2], `transaction operation ${operation.id} ${field2}`); + } + } + if (operation.type === "write") { + if (operation.staged === void 0 || operation.source !== void 0) { + throw new Error(`write operation ${operation.id} requires staged and forbids source`); + } + } else if (operation.type === "remove") { + if (operation.source !== void 0 || operation.staged !== void 0) { + throw new Error(`remove operation ${operation.id} forbids source and staged`); + } + } else if (operation.source === void 0 || operation.staged !== void 0 || operation.backup !== void 0) { + throw new Error(`move operation ${operation.id} requires source and forbids staged and backup`); + } + return operation; +} +function parseNativeArchiveTransactionJournalV2(value) { + const journal = record2(value, "Native Archive transaction journal"); + rejectUnknown2(journal, ARCHIVE_V2_JOURNAL_KEYS, "Native Archive transaction journal"); + if (journal.schema !== "comet.native.transaction.v2") { + throw new Error("Unsupported Native Archive transaction schema"); + } + if (typeof journal.id !== "string" || !/^[a-f0-9-]{8,}$/u.test(journal.id)) { + throw new Error("Native Archive transaction id is invalid"); + } + if (journal.kind !== "archive") throw new Error("Native v2 transaction kind must be archive"); + if (typeof journal.status !== "string" || !TRANSACTION_STATUSES.has(journal.status)) { + throw new Error("Native Archive transaction status is invalid"); + } + if (typeof journal.change !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(journal.change)) { + throw new Error("Native Archive transaction change name is invalid"); + } + if (!validTimestamp(journal.createdAt)) { + throw new Error("Native Archive transaction createdAt is invalid"); + } + assertHash(journal.preflightHash, "Native Archive transaction preflightHash"); + if (!Array.isArray(journal.operations) || journal.operations.length > 65) { + throw new Error("Native Archive transaction operations must be an array"); + } + const operations = journal.operations.map((value2, index) => { + const operation = record2(value2, `Archive transaction operations[${index}]`); + rejectUnknown2(operation, ARCHIVE_V2_OPERATION_KEYS, `Archive transaction operations[${index}]`); + if (typeof operation.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(operation.id)) { + throw new Error(`Archive transaction operations[${index}].id is invalid`); + } + if (operation.type !== "write" && operation.type !== "remove" && operation.type !== "move") { + throw new Error(`Archive transaction operation ${operation.id} has an invalid type`); + } + assertArchiveRef(operation.target, `Archive transaction operation ${operation.id} target`); + for (const field2 of ["source", "staged", "backup"]) { + if (operation[field2] !== void 0) { + assertArchiveRef( + operation[field2], + `Archive transaction operation ${operation.id} ${field2}` + ); + } + } + if (operation.expectedTargetHash !== null) { + assertHash( + operation.expectedTargetHash, + `Archive transaction operation ${operation.id} expectedTargetHash` + ); + } + if (operation.type === "write") { + if (operation.staged === void 0 || operation.source !== void 0 || operation.expectedSourceHash !== void 0) { + throw new Error( + `Archive write operation ${operation.id} requires staged and forbids source` + ); + } + assertHash(operation.stagedHash, `Archive write operation ${operation.id} stagedHash`); + if (operation.expectedTargetHash === null !== (operation.backup === void 0)) { + throw new Error( + `Archive write operation ${operation.id} backup must match target existence` + ); + } + } else if (operation.type === "remove") { + if (operation.source !== void 0 || operation.staged !== void 0 || operation.stagedHash !== void 0 || operation.expectedSourceHash !== void 0 || operation.backup === void 0 || operation.expectedTargetHash === null) { + throw new Error( + `Archive remove operation ${operation.id} requires a bound target and backup` + ); + } + } else { + if (operation.source === void 0 || operation.staged !== void 0 || operation.stagedHash !== void 0 || operation.backup !== void 0 || operation.expectedTargetHash !== null) { + throw new Error( + `Archive move operation ${operation.id} requires source and an absent target` + ); + } + assertHash( + operation.expectedSourceHash, + `Archive move operation ${operation.id} expectedSourceHash` + ); + } + return operation; + }); + const operationIds = operations.map((operation) => operation.id); + if (new Set(operationIds).size !== operationIds.length) { + throw new Error("Native Archive transaction operation ids must be unique"); + } + const transactionPrefix = `runtime/transactions/${journal.id}`; + const archiveMoves = operations.filter((operation) => operation.type === "move"); + if (archiveMoves.length !== 1 || archiveMoves[0].id !== "archive-change" || archiveMoves[0].source !== `changes/${journal.change}` || !new RegExp(`^archive/\\d{4}-\\d{2}-\\d{2}-${journal.change}$`, "u").test( + archiveMoves[0].target + ) || operations.at(-1) !== archiveMoves[0]) { + throw new Error("Native Archive transaction must end with its exact change move"); + } + const specTargets = /* @__PURE__ */ new Set(); + for (const operation of operations.slice(0, -1)) { + if (operation.type === "move" || !/^specs\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/spec\.md$/u.test(operation.target) || specTargets.has(operation.target)) { + throw new Error(`Native Archive transaction spec target is invalid: ${operation.target}`); + } + specTargets.add(operation.target); + if (operation.staged !== void 0 && !operation.staged.startsWith(`${transactionPrefix}/staged/specs/`)) { + throw new Error(`Native Archive transaction staged ref is invalid: ${operation.staged}`); + } + if (operation.backup !== void 0 && !operation.backup.startsWith(`${transactionPrefix}/backups/specs/`)) { + throw new Error(`Native Archive transaction backup ref is invalid: ${operation.backup}`); + } + } + return { + schema: "comet.native.transaction.v2", + id: journal.id, + kind: "archive", + status: journal.status, + change: journal.change, + createdAt: journal.createdAt, + preflightHash: journal.preflightHash, + operations + }; +} +function parseJournal(value) { + const journal = record2(value, "Native transaction journal"); + if (journal.schema === "comet.native.transaction.v2") { + return parseNativeArchiveTransactionJournalV2(journal); + } + rejectUnknown2(journal, JOURNAL_KEYS, "Native transaction journal"); + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Unsupported Native transaction schema"); + } + if (typeof journal.id !== "string" || !/^[a-f0-9-]{8,}$/u.test(journal.id)) { + throw new Error("Native transaction id is invalid"); + } + if (journal.kind !== "archive" && journal.kind !== "root-move") { + throw new Error("Native transaction kind is invalid"); + } + if (typeof journal.status !== "string" || !TRANSACTION_STATUSES.has(journal.status)) { + throw new Error("Native transaction status is invalid"); + } + if (typeof journal.projectRoot !== "string" || !path8.isAbsolute(journal.projectRoot) || typeof journal.nativeRoot !== "string" || !path8.isAbsolute(journal.nativeRoot)) { + throw new Error("Native transaction roots must be absolute paths"); + } + if (journal.change !== void 0 && (typeof journal.change !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(journal.change))) { + throw new Error("Native transaction change name is invalid"); + } + if (!validTimestamp(journal.createdAt)) { + throw new Error("Native transaction createdAt is invalid"); + } + if (!Array.isArray(journal.operations)) { + throw new Error("Native transaction operations must be an array"); + } + const operations = journal.operations.map(parseOperation); + const operationIds = operations.map((operation) => operation.id); + if (new Set(operationIds).size !== operationIds.length) { + throw new Error("Native transaction operation ids must be unique"); + } + return { + schema: "comet.native.transaction.v1", + id: journal.id, + kind: journal.kind, + status: journal.status, + projectRoot: journal.projectRoot, + nativeRoot: journal.nativeRoot, + ...typeof journal.change === "string" ? { change: journal.change } : {}, + createdAt: journal.createdAt, + operations + }; +} +function parseEvent(value, line) { + const event = record2(value, `Native transaction event at line ${line}`); + rejectUnknown2(event, EVENT_KEYS, `Native transaction event at line ${line}`); + if (event.sequence !== line) { + throw new Error(`Native transaction event sequence at line ${line} must be ${line}`); + } + if (!validTimestamp(event.timestamp)) { + throw new Error(`Native transaction event timestamp at line ${line} is invalid`); + } + if (typeof event.type !== "string" || !EVENT_TYPES.has(event.type)) { + throw new Error(`Native transaction event type at line ${line} is invalid`); + } + const operationEvent = event.type === "operation-started" || event.type === "operation-completed"; + if (operationEvent && (typeof event.operationId !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(event.operationId) || Buffer.byteLength(event.operationId, "utf8") > 256) || !operationEvent && event.operationId !== void 0) { + throw new Error(`Native transaction event operationId at line ${line} is invalid`); + } + return { + sequence: event.sequence, + timestamp: event.timestamp, + type: event.type, + ...typeof event.operationId === "string" ? { operationId: event.operationId } : {} + }; +} +function matchesIsoTimestampPrefix(value) { + if (value.length > 24) return false; + const shape = "0000-00-00T00:00:00.000Z"; + for (let index = 0; index < value.length; index += 1) { + const expected = shape[index]; + const actual = value[index]; + if (expected === "0") { + if (!/[0-9]/u.test(actual)) return false; + } else if (actual !== expected) { + return false; + } + } + const numericRanges = [ + [5, 2, 1, 12], + [8, 2, 1, 31], + [11, 2, 0, 23], + [14, 2, 0, 59], + [17, 2, 0, 59], + [20, 3, 0, 999] + ]; + for (const [offset, width, minimum, maximum] of numericRanges) { + if (value.length <= offset) continue; + const partial = value.slice(offset, Math.min(offset + width, value.length)); + const completable = Array.from( + { length: maximum - minimum + 1 }, + (_entry, index) => String(minimum + index).padStart(width, "0") + ).some((candidate) => candidate.startsWith(partial)); + if (!completable) return false; + } + if (value.length >= 10 && !validTimestamp(`${value.slice(0, 10)}T00:00:00.000Z`)) { + return false; + } + return value.length < 24 || validTimestamp(value); +} +function isStrictLiteralPrefix(value, expected) { + return value.length < expected.length && expected.startsWith(value); +} +function couldCompleteOperationEventSuffix(value) { + const operationPrefix = '","operationId":"'; + if (isStrictLiteralPrefix(value, operationPrefix)) return true; + if (!value.startsWith(operationPrefix)) return false; + const remainder = value.slice(operationPrefix.length); + const closingQuote = remainder.indexOf('"'); + if (closingQuote === -1) { + return remainder.length === 0 || /^[a-z0-9][a-z0-9-]*$/u.test(remainder); + } + const operationId = remainder.slice(0, closingQuote); + if (!/^[a-z0-9][a-z0-9-]*$/u.test(operationId)) return false; + return isStrictLiteralPrefix(remainder.slice(closingQuote), '"}'); +} +function isRecognizedIncompleteEventTail(source, sequence) { + if (source.length === 0 || Buffer.byteLength(source, "utf8") > NATIVE_TRANSACTION_EVENT_MAX_BYTES) { + return false; + } + const prefix = `{"sequence":${sequence},"timestamp":"`; + if (isStrictLiteralPrefix(source, prefix)) return true; + if (!source.startsWith(prefix)) return false; + const afterPrefix = source.slice(prefix.length); + const timestamp2 = afterPrefix.slice(0, Math.min(24, afterPrefix.length)); + if (!matchesIsoTimestampPrefix(timestamp2)) return false; + if (afterPrefix.length < 24) return true; + if (!validTimestamp(timestamp2)) return false; + const typePrefix = '","type":"'; + const afterTimestamp = afterPrefix.slice(24); + if (isStrictLiteralPrefix(afterTimestamp, typePrefix)) return true; + if (!afterTimestamp.startsWith(typePrefix)) return false; + const typeAndSuffix = afterTimestamp.slice(typePrefix.length); + for (const type of EVENT_TYPES) { + if (isStrictLiteralPrefix(typeAndSuffix, type)) return true; + if (!typeAndSuffix.startsWith(type)) continue; + const suffix = typeAndSuffix.slice(type.length); + if (type === "operation-started" || type === "operation-completed") { + if (couldCompleteOperationEventSuffix(suffix)) return true; + } else if (isStrictLiteralPrefix(suffix, '"}')) { + return true; + } + } + return false; +} +function parseEventLine(source, line) { + if (source.length === 0) throw new Error("Blank transaction event line"); + if (Buffer.byteLength(source, "utf8") > NATIVE_TRANSACTION_EVENT_MAX_BYTES) { + throw new Error(`Native transaction event at line ${line} is too large`); + } + return parseEvent(JSON.parse(source), line); +} +function parseEventLogSource(source) { + const entries = source.split("\n"); + const terminated = entries.at(-1) === ""; + if (terminated) entries.pop(); + if (entries.length > NATIVE_TRANSACTION_EVENT_MAX_COUNT) { + throw new Error( + `Native transaction event log exceeds ${NATIVE_TRANSACTION_EVENT_MAX_COUNT} events` + ); + } + const events = []; + for (let index = 0; index < entries.length; index += 1) { + const line = index + 1; + const raw = entries[index]; + const entry2 = raw.endsWith("\r") ? raw.slice(0, -1) : raw; + const finalUnterminated = !terminated && index === entries.length - 1; + try { + events.push(parseEventLine(entry2, line)); + } catch (error) { + if (finalUnterminated) { + let syntacticallyComplete = false; + try { + JSON.parse(entry2); + syntacticallyComplete = true; + } catch { + } + if (!syntacticallyComplete && isRecognizedIncompleteEventTail(entry2, line)) break; + } + throw new Error(`Invalid Native transaction event at line ${line}`, { cause: error }); + } + } + return events; +} +function canonicalEventLogSource(events) { + return events.length === 0 ? "" : `${events.map((event) => JSON.stringify(event)).join("\n")} +`; +} +function transactionDir(paths, id) { + if (!/^[a-f0-9-]{8,}$/u.test(id)) throw new Error(`Invalid Native transaction id: ${id}`); + return path8.join(paths.transactionsDir, id); +} +function nativeTransactionPaths(paths, id) { + const directory = transactionDir(paths, id); + return { + directory, + journal: path8.join(directory, "transaction.json"), + events: path8.join(directory, "events.jsonl"), + staged: path8.join(directory, "staged"), + backups: path8.join(directory, "backups") + }; +} +async function resolveNativeTransactionPaths(paths, id) { + const transaction = nativeTransactionPaths(paths, id); + await Promise.all( + Object.values(transaction).map( + (target) => resolveContainedNativePath(paths.nativeRoot, target) + ) + ); + return transaction; +} +function resolveRefLexically(paths, ref) { + if (ref.length === 0 || path8.isAbsolute(ref) || /^(?:[A-Za-z]:|~|[\\/])/u.test(ref) || ref.split(/[\\/]/u).includes("..")) { + throw new Error(`Unsafe Native transaction ref: ${ref}`); + } + const target = path8.resolve(paths.nativeRoot, ...ref.split(/[\\/]/u)); + if (!isInsidePath(paths.nativeRoot, target)) + throw new Error(`Unsafe Native transaction ref: ${ref}`); + return target; +} +async function resolveRef(paths, ref) { + return resolveContainedNativePath(paths.nativeRoot, resolveRefLexically(paths, ref)); +} +async function exists(file) { + try { + await fs8.access(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function readEventLogSnapshot(paths, id, options = {}) { + const tx = await resolveNativeTransactionPaths(paths, id); + try { + await fs8.lstat(tx.events); + } catch (error) { + if (error.code === "ENOENT") { + return { + exists: false, + hash: null, + size: 0, + events: [], + canonicalSource: "", + needsRepair: false + }; + } + throw error; + } + try { + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.events, + maxBytes: NATIVE_TRANSACTION_EVENTS_MAX_BYTES, + label: `Native transaction event log ${id}`, + hooks: options.hooks + }); + let source; + try { + source = UTF8_DECODER.decode(snapshot2.bytes); + } catch (error) { + throw new Error(`Native transaction event log ${id} is not valid UTF-8`, { cause: error }); + } + const events = parseEventLogSource(source); + const canonicalSource = canonicalEventLogSource(events); + return { + exists: true, + hash: snapshot2.hash, + size: snapshot2.size, + events, + canonicalSource, + needsRepair: source !== canonicalSource + }; + } catch (error) { + if (error.code === "ENOENT") { + throw new Error(`Native transaction event log ${id} changed while reading`, { + cause: error + }); + } + throw error; + } +} +async function assertEventLogSnapshotUnchanged(paths, id, expected) { + const actual = await readEventLogSnapshot(paths, id); + if (actual.exists !== expected.exists || actual.hash !== expected.hash || actual.size !== expected.size) { + throw new Error(`Native transaction event log ${id} changed before append`); + } +} +async function appendNativeTransactionEvent(paths, id, type, operationId) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readEventLogSnapshot(paths, id); + const existing = snapshot2.events.find( + (event2) => event2.type === type && event2.operationId === operationId + ); + if (existing) { + if (snapshot2.needsRepair) { + await atomicWriteText(tx.events, snapshot2.canonicalSource, { + containedRoot: paths.nativeRoot, + beforeCommit: () => assertEventLogSnapshotUnchanged(paths, id, snapshot2) + }); + } + return existing; + } + if (snapshot2.events.length >= NATIVE_TRANSACTION_EVENT_MAX_COUNT) { + throw new Error( + `Native transaction event log ${id} exceeds ${NATIVE_TRANSACTION_EVENT_MAX_COUNT} events` + ); + } + const event = { + sequence: snapshot2.events.length + 1, + timestamp: (/* @__PURE__ */ new Date()).toISOString(), + type, + ...operationId ? { operationId } : {} + }; + parseEvent(event, event.sequence); + await fs8.mkdir(tx.directory, { recursive: true }); + await atomicWriteText(tx.events, canonicalEventLogSource([...snapshot2.events, event]), { + containedRoot: paths.nativeRoot, + beforeCommit: () => assertEventLogSnapshotUnchanged(paths, id, snapshot2) + }); + return event; +} +async function createNativeTransaction(paths, journal) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + journal = parseJournal(journal); + const tx = await resolveNativeTransactionPaths(paths, journal.id); + await fs8.mkdir(tx.staged, { recursive: true }); + await fs8.mkdir(tx.backups, { recursive: true }); + await atomicWriteJson(tx.journal, journal); + await appendNativeTransactionEvent(paths, journal.id, "prepared"); +} +async function readNativeTransaction(paths, id, options = {}) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.journal, + maxBytes: NATIVE_TRANSACTION_JOURNAL_MAX_BYTES, + label: `Native transaction journal ${id}`, + hooks: options.hooks + }); + const value = JSON.parse(UTF8_DECODER.decode(snapshot2.bytes)); + const journal = parseJournal(value); + if (journal.id !== id) { + throw new Error(`Invalid Native transaction journal: ${id}`); + } + return journal; +} +async function readNativeTransactionEvents(paths, id, options = {}) { + return (await readEventLogSnapshot(paths, id, options)).events; +} +async function setNativeTransactionStatus(paths, journal, status) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + const updated = parseJournal({ ...journal, status }); + await atomicWriteJson((await resolveNativeTransactionPaths(paths, journal.id)).journal, updated); + return updated; +} +async function readLegacyTransactionFile(paths, file, label) { + try { + return await readNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + label + }); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function copyAtomic(paths, source, target, label) { + const sourceSnapshot = await readLegacyTransactionFile(paths, source, `${label} source`); + if (!sourceSnapshot) throw new Error(`${label} source does not exist`); + const targetSnapshot = await readLegacyTransactionFile(paths, target, `${label} target`); + await ensureNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: path8.dirname(target), + label: `${label} target parent` + }); + await copyNativeProtectedFile({ + sourceRoot: paths.nativeRoot, + source, + targetRoot: paths.nativeRoot, + target, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + label, + expectedHash: sourceSnapshot.hash, + expectedTargetHash: targetSnapshot?.hash ?? null, + exclusive: targetSnapshot === null + }); +} +async function removeLegacyTransactionFile(paths, file, label) { + const snapshot2 = await readLegacyTransactionFile(paths, file, label); + if (!snapshot2) return; + await removeNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + expectedHash: snapshot2.hash, + expectedSize: snapshot2.size, + label + }); +} +async function backupTarget(paths, operation) { + if (!operation.backup) return; + const target = await resolveRef(paths, operation.target); + const backup = await resolveRef(paths, operation.backup); + if (!await exists(target) || await exists(backup)) return; + await copyAtomic(paths, target, backup, `Legacy Native transaction backup ${operation.id}`); +} +async function applyOperation(paths, operation) { + const target = await resolveRef(paths, operation.target); + if (operation.type === "write") { + if (!operation.staged) throw new Error(`Write operation ${operation.id} has no staged ref`); + await backupTarget(paths, operation); + await copyAtomic( + paths, + await resolveRef(paths, operation.staged), + target, + `Legacy Native transaction write ${operation.id}` + ); + return; + } + if (operation.type === "remove") { + await backupTarget(paths, operation); + await removeLegacyTransactionFile( + paths, + target, + `Legacy Native transaction remove ${operation.id}` + ); + return; + } + if (!operation.source) throw new Error(`Move operation ${operation.id} has no source ref`); + const source = await resolveRef(paths, operation.source); + const [sourceExists, targetExists] = await Promise.all([exists(source), exists(target)]); + if (!sourceExists && targetExists) { + const targetDirectory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: target, + label: `Legacy Native transaction move target ${operation.id}`, + maxEntries: NATIVE_LEGACY_TRANSACTION_DIRECTORY_MAX_ENTRIES + }); + await targetDirectory.verify(); + return; + } + if (targetExists) throw new Error(`Move target already exists: ${operation.target}`); + if (!sourceExists) throw new Error(`Move source does not exist: ${operation.source}`); + await moveNativeProtectedDirectory({ + root: paths.nativeRoot, + source, + target, + label: `Legacy Native transaction move ${operation.id}` + }); +} +async function applyNativeTransaction(paths, journal, hooks) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + let current = journal.status === "prepared" ? await setNativeTransactionStatus(paths, journal, "applying") : journal; + const events = await readNativeTransactionEvents(paths, journal.id); + const completed = new Set( + events.filter((event) => event.type === "operation-completed").map((event) => event.operationId) + ); + let completedCount = completed.size; + for (const operation of current.operations) { + if (completed.has(operation.id)) continue; + await appendNativeTransactionEvent(paths, current.id, "operation-started", operation.id); + await applyOperation(paths, operation); + await appendNativeTransactionEvent(paths, current.id, "operation-completed", operation.id); + completedCount += 1; + await hooks?.afterOperation?.(operation, completedCount); + } + current = await readNativeTransaction(paths, current.id); + return current; +} +async function rollbackOperation(paths, operation) { + const target = await resolveRef(paths, operation.target); + const backup = operation.backup ? await resolveRef(paths, operation.backup) : null; + if (operation.type === "move") { + if (!operation.source) throw new Error(`Move operation ${operation.id} has no source ref`); + const source = await resolveRef(paths, operation.source); + if (await exists(target)) { + if (await exists(source)) { + throw new Error(`Legacy Native rollback source already exists: ${operation.source}`); + } + await moveNativeProtectedDirectory({ + root: paths.nativeRoot, + source: target, + target: source, + label: `Legacy Native transaction rollback move ${operation.id}` + }); + } + return; + } + if (backup && await exists(backup)) { + await copyAtomic( + paths, + backup, + target, + `Legacy Native transaction rollback restore ${operation.id}` + ); + } else { + await removeLegacyTransactionFile( + paths, + target, + `Legacy Native transaction rollback remove ${operation.id}` + ); + } +} +async function rollbackNativeTransaction(paths, journal) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some( + (event) => event.type === "archive-finalization-started" || event.type === "archive-finalized" + )) { + throw new Error("An archive whose finalization started can only be recovered by continuing it"); + } + let current = await setNativeTransactionStatus(paths, journal, "rolling-back"); + await appendNativeTransactionEvent(paths, current.id, "rollback-started"); + const started = new Set( + events.filter((event) => event.type === "operation-started" || event.type === "operation-completed").map((event) => event.operationId) + ); + for (const operation of [...current.operations].reverse()) { + if (started.has(operation.id)) await rollbackOperation(paths, operation); + } + await appendNativeTransactionEvent(paths, current.id, "rollback-completed"); + current = await setNativeTransactionStatus(paths, current, "rolled-back"); + return current; +} +async function finalizeNativeTransaction(paths, journal, event) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + await appendNativeTransactionEvent(paths, journal.id, event); + return event === "commit" ? setNativeTransactionStatus(paths, journal, "committed") : journal; +} +function nativeRootRef(paths, target) { + const absolute = path8.resolve(target); + if (!isInsidePath(paths.nativeRoot, absolute)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + return path8.relative(paths.nativeRoot, absolute).split(path8.sep).join("/"); +} + +// domains/comet-native/native-mutation-lock.ts +async function hasUnfinishedTransaction(paths, allowedTransactionId) { + let entries; + try { + entries = await fs9.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + try { + const transaction = await readNativeTransaction(paths, entry2.name); + if (transaction.id !== allowedTransactionId && transaction.status !== "committed" && transaction.status !== "rolled-back") { + return true; + } + } catch { + return true; + } + } + return false; +} +async function acquireNativeMutationLock(paths, operation) { + const deadline = Date.now() + 5e3; + const file = path9.join(paths.locksDir, "root-move.lock"); + while (true) { + try { + return await acquireNativeLock(paths, "root-move", operation); + } catch (error) { + const cause = error.cause; + if (cause?.code !== "EEXIST") throw error; + const diagnosis = await diagnoseNativeLock(file); + if (diagnosis.status === "missing") continue; + if (diagnosis.status !== "active" || Date.now() >= deadline) throw error; + await new Promise((resolve) => setTimeout(resolve, 5 + Math.floor(Math.random() * 11))); + } + } +} +async function withNativeMutationLock(paths, operation, work, options) { + const lock = await acquireNativeMutationLock(paths, operation); + try { + await assertNoPendingNativeRootMove(paths.projectRoot); + if (await hasUnfinishedTransaction(paths, options?.allowedTransactionId)) { + throw new Error("Native transaction recovery is required before another mutation"); + } + return await work(); + } finally { + await releaseNativeLock(lock); + } +} + +// domains/comet-native/native-revision.ts +function validRevision(value) { + return Number.isSafeInteger(value) && value >= 1; +} +async function compareAndSwapNativeRevision(options) { + if (!validRevision(options.expectedRevision)) { + throw new Error("Native expected revision must be a positive integer"); + } + if (options.next.revision !== options.expectedRevision + 1) { + throw new Error("Native CAS next revision must increment the expected revision exactly once"); + } + const current = await options.read(); + if (!validRevision(current.revision)) { + throw new Error("Native current revision must be a positive integer"); + } + const equals = options.equals ?? ((left, right) => JSON.stringify(left) === JSON.stringify(right)); + if (current.revision === options.next.revision && equals(current, options.next)) { + return current; + } + if (current.revision !== options.expectedRevision) { + throw options.conflict(current.revision); + } + await options.write(options.next); + return options.next; +} + +// domains/comet-native/native-snapshot.ts +import { createHash as createHash5 } from "crypto"; +import { spawn } from "node:child_process"; +import { promises as fs10 } from "fs"; +import path10 from "path"; + +// domains/comet-native/native-hash.ts +import { createHash as createHash4 } from "crypto"; +import { createReadStream } from "fs"; +async function sha256File(file) { + const hash6 = createHash4("sha256"); + for await (const chunk of createReadStream(file)) hash6.update(chunk); + return hash6.digest("hex"); +} +function sha256Text(content) { + return createHash4("sha256").update(content).digest("hex"); +} + +// domains/comet-native/native-snapshot.ts +var DEFAULT_NATIVE_SNAPSHOT_LIMITS = { + maxFiles: 1e4, + maxFileBytes: 5 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxManifestBytes: 1024 * 1024 +}; +var MAX_RECORDED_OMISSIONS = 1e3; +var NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES = 8 * 1024 * 1024; +var CHANGE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var MANIFEST_KEYS = /* @__PURE__ */ new Set([ + "schema", + "origin", + "capture", + "createdAt", + "complete", + "limits", + "entries", + "omitted", + "omittedCount", + "omissionOverflow", + "policy" +]); +var LIMIT_KEYS = /* @__PURE__ */ new Set([ + "maxFiles", + "maxFileBytes", + "maxTotalBytes", + "maxManifestBytes", + "maxDurationMs" +]); +var POLICY_KEYS = /* @__PURE__ */ new Set(["schema", "include", "exclude", "hash"]); +var CAPTURE_KEYS = /* @__PURE__ */ new Set(["provider", "gitSelection", "physicalSelection", "projection"]); +var GIT_PROJECTION_KEYS = /* @__PURE__ */ new Set(["provider", "selection"]); +var GIT_SELECTION_KEYS = /* @__PURE__ */ new Set([ + "schema", + "status", + "stageBefore", + "combined", + "stageAfter", + "finalStageBefore", + "finalCombined", + "finalStageAfter" +]); +var GIT_SELECTION_STREAM_KEYS = /* @__PURE__ */ new Set([ + "hash", + "recordCount", + "storedRecordCount", + "stdoutBytes", + "overflow" +]); +var PHYSICAL_SELECTION_KEYS = /* @__PURE__ */ new Set(["schema", "status", "before", "after"]); +var PHYSICAL_SELECTION_STREAM_KEYS = /* @__PURE__ */ new Set([ + "hash", + "visitedNodeCount", + "recordCount", + "storedRecordCount", + "encodedBytes", + "overflow", + "unstable" +]); +var ENTRY_KEYS = /* @__PURE__ */ new Set(["path", "hash", "size", "type"]); +var OMISSION_KEYS = /* @__PURE__ */ new Set(["path", "size", "type", "reason"]); +var OMISSION_OVERFLOW_KEYS = /* @__PURE__ */ new Set(["ref", "hash", "count"]); +var SNAPSHOT_ORIGINS = /* @__PURE__ */ new Set([ + "change-created", + "legacy-migration", + "explicit" +]); +var OMISSION_TYPES = /* @__PURE__ */ new Set(["file", "directory", "other"]); +var OMISSION_REASONS = /* @__PURE__ */ new Set([ + "file-size", + "file-count", + "total-size", + "manifest-size", + "changed-during-read", + "unreadable", + "gitlink-unavailable", + "gitlink-dirty", + "gitlink-changed", + "legacy-gitlink-boundary", + "git-enumeration-limit", + "git-selection-changed", + "physical-enumeration-limit", + "physical-selection-changed" +]); +var HASH_PATTERN = /^[a-f0-9]{64}$/u; +var GIT_OBJECT_ID_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u; +var UNREADABLE_ERROR_CODES = /* @__PURE__ */ new Set(["EACCES", "EPERM"]); +var GIT_LIST_STDERR_LIMIT = 64 * 1024; +var GIT_TEXT_STDOUT_LIMIT = 64 * 1024; +var DEFAULT_NATIVE_SNAPSHOT_EXECUTION_BUDGET_MS = 6e4; +var WINDOWS_TASKKILL_ATTEMPT_MS = 1e3; +var WINDOWS_TASKKILL_ATTEMPTS = 2; +var DEFAULT_NATIVE_GIT_SELECTION_LIMITS = { + maxRecords: 2e4, + maxBytes: 8 * 1024 * 1024, + maxRecordBytes: 64 * 1024 +}; +var DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS = { + maxNodes: 2e4, + maxBytes: 8 * 1024 * 1024, + maxPathBytes: 64 * 1024 +}; +function createNativeSnapshotExecution(options) { + const deadlineMs = options.deadlineMs ?? DEFAULT_NATIVE_SNAPSHOT_EXECUTION_BUDGET_MS; + if (!Number.isSafeInteger(deadlineMs) || deadlineMs < 1) { + throw new Error("Native snapshot execution budget must be a positive integer"); + } + return { + deadlineAt: Date.now() + deadlineMs, + gitProcess: options.gitProcess ?? { command: "git" } + }; +} +function remainingNativeSnapshotTime(execution) { + return Math.max(0, execution.deadlineAt - Date.now()); +} +function nativeSnapshotExecutionHasBudget(execution) { + return remainingNativeSnapshotTime(execution) >= 1; +} +function nativeGitSnapshotTimeoutError(cause) { + const error = cause === void 0 ? new Error("Native snapshot deadline exceeded while waiting for Git") : new Error("Native snapshot deadline exceeded while waiting for Git", { cause }); + return Object.assign(error, { code: "GIT_SNAPSHOT_TIMEOUT" }); +} +function isNativeGitSnapshotTimeout(error) { + return error.code === "GIT_SNAPSHOT_TIMEOUT"; +} +function resolveNativePhysicalSelectionLimits(values) { + const limits = { ...DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS, ...values }; + if (!Number.isSafeInteger(limits.maxNodes) || limits.maxNodes < 1 || !Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 1 || !Number.isSafeInteger(limits.maxPathBytes) || limits.maxPathBytes < 1 || limits.maxPathBytes > limits.maxBytes) { + throw new Error("Native physical selection limits must be positive bounded integers"); + } + return limits; +} +async function terminateNativeProcessTree(child, adapter) { + if (adapter.terminateTree) { + await adapter.terminateTree(child); + return; + } + const pid = child.pid; + if (pid === void 0) { + child.kill("SIGKILL"); + return; + } + if (process.platform === "win32") { + const configuredSystemRoot = process.env.SystemRoot ?? process.env.WINDIR; + const systemRoot = configuredSystemRoot && path10.win32.isAbsolute(configuredSystemRoot) ? path10.win32.resolve(configuredSystemRoot) : "C:\\Windows"; + const taskkill = path10.win32.join(systemRoot, "System32", "taskkill.exe"); + if (!path10.win32.isAbsolute(taskkill)) { + child.kill("SIGKILL"); + child.stdin?.destroy(); + child.stdout?.destroy(); + child.stderr?.destroy(); + throw Object.assign(new Error("Native Git taskkill path is not trusted"), { + code: "GIT_SNAPSHOT_TERMINATION_UNCONFIRMED" + }); + } + const runTaskkillAttempt = () => new Promise((resolve) => { + let finished = false; + let killer = null; + let fallbackTimer = null; + const finish = (confirmed, terminateKiller = false) => { + if (finished) return; + finished = true; + if (fallbackTimer) clearTimeout(fallbackTimer); + if (terminateKiller) killer?.kill("SIGKILL"); + resolve(confirmed); + }; + try { + killer = spawn(taskkill, ["/pid", String(pid), "/t", "/f"], { + stdio: "ignore", + windowsHide: true + }); + killer.once("error", () => finish(false)); + killer.once("close", (code) => finish(code === 0)); + fallbackTimer = setTimeout(() => finish(false, true), WINDOWS_TASKKILL_ATTEMPT_MS); + fallbackTimer.unref(); + } catch { + finish(false); + } + }); + for (let attempt = 0; attempt < WINDOWS_TASKKILL_ATTEMPTS; attempt += 1) { + if (await runTaskkillAttempt()) { + child.kill("SIGKILL"); + return; + } + } + child.kill("SIGKILL"); + child.stdin?.destroy(); + child.stdout?.destroy(); + child.stderr?.destroy(); + throw Object.assign( + new Error("Native Git process-tree termination could not be confirmed on Windows"), + { code: "GIT_SNAPSHOT_TERMINATION_UNCONFIRMED" } + ); + } + try { + process.kill(-pid, "SIGKILL"); + } catch (error) { + if (error.code !== "ESRCH") throw error; + } + child.kill("SIGKILL"); +} +function startNativeGitProcess(execution, projectRoot, args, input) { + const remaining = remainingNativeSnapshotTime(execution); + if (remaining < 1) throw nativeGitSnapshotTimeoutError(); + const adapter = execution.gitProcess; + const child = spawn( + adapter.command, + [...adapter.argsPrefix ?? [], "-C", projectRoot, ...args], + { + stdio: [input ? "pipe" : "ignore", "pipe", "pipe"], + windowsHide: true, + detached: process.platform !== "win32" + } + ); + let spawnError = null; + let timedOut = false; + let termination = null; + child.once("error", (error) => { + spawnError = error; + }); + const close = new Promise((resolve) => { + child.once("close", resolve); + }); + const timer = setTimeout(() => { + timedOut = true; + termination = terminateNativeProcessTree(child, adapter).then( + () => ({ error: null }), + (error) => ({ error }) + ); + }, remaining); + timer.unref(); + const completion = close.then(async (code) => { + clearTimeout(timer); + if (timedOut) { + const result2 = await termination; + if (result2?.error) throw nativeGitSnapshotTimeoutError(result2.error); + throw nativeGitSnapshotTimeoutError(); + } + return { code, spawnError }; + }); + return { child, completion }; +} +function resolveNativeGitSelectionLimits(values) { + const limits = { ...DEFAULT_NATIVE_GIT_SELECTION_LIMITS, ...values }; + if (!Number.isSafeInteger(limits.maxRecords) || limits.maxRecords < 1 || !Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 1 || !Number.isSafeInteger(limits.maxRecordBytes) || limits.maxRecordBytes < 1 || limits.maxRecordBytes > limits.maxBytes) { + throw new Error("Native Git selection limits must be positive bounded integers"); + } + return limits; +} +async function runGitBoundedOutput(execution, projectRoot, args, maxBytes = GIT_TEXT_STDOUT_LIMIT) { + const { child, completion } = startNativeGitProcess(execution, projectRoot, args, false); + const stdout = []; + const stderr = []; + let stderrBytes = 0; + let stdoutBytes = 0; + let stdoutOverflow = false; + child.stdout.on("data", (chunk) => { + const remaining = Math.max(0, maxBytes - stdoutBytes); + if (remaining > 0) stdout.push(Buffer.from(chunk).subarray(0, remaining)); + stdoutBytes += chunk.byteLength; + if (stdoutBytes > maxBytes) stdoutOverflow = true; + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + const { code, spawnError } = await completion; + if (code === 0 && spawnError === null && !stdoutOverflow) return Buffer.concat(stdout); + throw Object.assign( + new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ), + { code: "GIT_SNAPSHOT_UNAVAILABLE" } + ); +} +function gitSelectionStreamEvidence(result2) { + return { + hash: result2.digest, + recordCount: result2.recordCount, + storedRecordCount: result2.records.length, + stdoutBytes: result2.stdoutBytes, + overflow: result2.overflow + }; +} +function runGitNullRecords(execution, projectRoot, args, options) { + if (options.outputChunkBytes !== void 0 && (!Number.isSafeInteger(options.outputChunkBytes) || options.outputChunkBytes < 1)) { + throw new Error("Native Git output chunk size must be a positive integer"); + } + return new Promise((resolve, reject) => { + const { child, completion } = startNativeGitProcess( + execution, + projectRoot, + args, + options.stdin !== void 0 + ); + const records = []; + const digest = createHash5("sha256"); + const stderr = []; + let stderrBytes = 0; + let stdoutBytes = 0; + let storedBytes = 0; + let recordCount = 0; + let pending = Buffer.alloc(0); + let droppingRecord = false; + let overflow = false; + let malformed = false; + const consumeChunk = (chunk) => { + let offset = 0; + while (offset < chunk.byteLength) { + const separator = chunk.indexOf(0, offset); + const end = separator < 0 ? chunk.byteLength : separator; + const part = chunk.subarray(offset, end); + if (!droppingRecord && part.byteLength > 0) { + if (pending.byteLength + part.byteLength > options.maxRecordBytes) { + pending = Buffer.alloc(0); + droppingRecord = true; + overflow = true; + } else { + pending = Buffer.concat([pending, part]); + } + } + if (separator < 0) break; + recordCount += 1; + if (pending.byteLength === 0 && !droppingRecord) malformed = true; + const recordBytes = pending.byteLength + 1; + if (!droppingRecord && recordCount <= options.maxRecords && storedBytes + recordBytes <= options.maxBytes) { + records.push(pending); + storedBytes += recordBytes; + } else { + overflow = true; + } + pending = Buffer.alloc(0); + droppingRecord = false; + offset = separator + 1; + } + }; + child.stdout.on("data", (chunk) => { + digest.update(chunk); + stdoutBytes += chunk.byteLength; + if (stdoutBytes > options.maxBytes) overflow = true; + const outputChunkBytes = options.outputChunkBytes; + if (outputChunkBytes === void 0) { + consumeChunk(chunk); + return; + } + for (let offset = 0; offset < chunk.byteLength; offset += outputChunkBytes) { + consumeChunk(chunk.subarray(offset, Math.min(chunk.byteLength, offset + outputChunkBytes))); + } + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + child.stdin?.on("error", () => { + }); + void completion.then(({ code, spawnError }) => { + const acceptedExitCodes = options.acceptedExitCodes ?? [0]; + if (code !== null && acceptedExitCodes.includes(code) && spawnError === null && !malformed && pending.byteLength === 0 && !droppingRecord) { + resolve({ + records, + digest: digest.digest("hex"), + overflow, + recordCount, + stdoutBytes + }); + return; + } + reject( + new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : malformed || pending.byteLength > 0 || droppingRecord ? ": malformed NUL-delimited output" : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ) + ); + }, reject); + if (options.stdin) child.stdin?.end(options.stdin); + }); +} +function decodeGitRecord(value) { + const decoded = value.toString("utf8"); + if (!Buffer.from(decoded, "utf8").equals(value)) { + throw new Error("Native Git snapshot provider returned non-UTF-8 path data"); + } + return decoded; +} +async function runGitCheckIgnore(execution, projectRoot, paths) { + if (paths.length === 0) return /* @__PURE__ */ new Set(); + const result2 = await runGitNullRecords( + execution, + projectRoot, + ["check-ignore", "--no-index", "-z", "--stdin"], + { + ...DEFAULT_NATIVE_GIT_SELECTION_LIMITS, + maxRecords: Math.max(1, paths.length), + acceptedExitCodes: [0, 1], + stdin: Buffer.from(`${paths.join("\0")}\0`, "utf8") + } + ); + if (result2.overflow) { + throw new Error("Native Git check-ignore output exceeded its safety budget"); + } + return new Set( + result2.records.map((record8) => { + const value = decodeGitRecord(record8); + return value.endsWith("/") ? value.slice(0, -1) : value; + }) + ); +} +async function runGitHasOutput(execution, projectRoot, args) { + const { child, completion } = startNativeGitProcess(execution, projectRoot, args, false); + const stderr = []; + let stderrBytes = 0; + let hasOutput = false; + child.stdout.on("data", (chunk) => { + if (chunk.byteLength > 0) hasOutput = true; + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + const { code, spawnError } = await completion; + if (code === 0 && spawnError === null) return hasOutput; + throw new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ); +} +function safeGitProjectPath(value) { + const withoutDirectoryMarker = value.endsWith("/") ? value.slice(0, -1) : value; + if (withoutDirectoryMarker.length === 0 || withoutDirectoryMarker.includes("\\") || path10.posix.isAbsolute(withoutDirectoryMarker) || /^[A-Za-z]:/u.test(withoutDirectoryMarker) || path10.posix.normalize(withoutDirectoryMarker) !== withoutDirectoryMarker || withoutDirectoryMarker === ".." || withoutDirectoryMarker.startsWith("../") || withoutDirectoryMarker.includes("\0")) { + return null; + } + return withoutDirectoryMarker; +} +function requireSafeGitProjectPaths(values, source) { + return values.map((value) => { + const relative = safeGitProjectPath(value); + if (relative === null) { + throw new Error(`Native Git snapshot provider returned an unsafe ${source} path`); + } + return relative; + }); +} +async function hasGitMetadataBoundary(projectRoot) { + let cursor = path10.resolve(projectRoot); + while (true) { + try { + await fs10.lstat(path10.join(cursor, ".git")); + return true; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const parent = path10.dirname(cursor); + if (parent === cursor) return false; + cursor = parent; + } +} +async function readNativeGitSelectionResults(execution, projectRoot, limits, hooks = {}) { + const options = { + ...limits, + ...hooks.outputChunkBytes === void 0 ? {} : { outputChunkBytes: hooks.outputChunkBytes } + }; + const stagedBefore = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--stage", "-z"], + options + ); + await hooks.afterStageBefore?.(); + const combined = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], + options + ); + await hooks.afterCombined?.(); + const stagedAfter = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--stage", "-z"], + options + ); + return { stagedBefore, combined, stagedAfter }; +} +function gitSelectionFence(results) { + return { + stageBefore: gitSelectionStreamEvidence(results.stagedBefore), + combined: gitSelectionStreamEvidence(results.combined), + stageAfter: gitSelectionStreamEvidence(results.stagedAfter) + }; +} +async function nativeGitSnapshotSelection(execution, projectRoot, limits = DEFAULT_NATIVE_GIT_SELECTION_LIMITS, hooks = {}) { + if (!await hasGitMetadataBoundary(projectRoot)) return null; + let insideWorktree; + try { + insideWorktree = await runGitBoundedOutput(execution, projectRoot, [ + "rev-parse", + "--is-inside-work-tree" + ]); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider could not inspect the repository boundary", { + cause: error + }); + } + if (insideWorktree.toString("utf8").trim() !== "true") { + throw new Error("Native Git snapshot provider found .git metadata outside a working tree"); + } + let results; + try { + results = await readNativeGitSelectionResults(execution, projectRoot, limits, hooks); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider failed after repository detection", { + cause: error + }); + } + const { stagedBefore, combined, stagedAfter } = results; + const tracked = /* @__PURE__ */ new Set(); + const gitlinks = /* @__PURE__ */ new Set(); + const addStagedRecords = (records) => { + for (const encoded of records) { + const record8 = decodeGitRecord(encoded); + const separator = record8.indexOf(" "); + if (separator < 0) { + throw new Error("Native Git snapshot provider returned a malformed staged record"); + } + const header = /^(?[0-7]{6}) (?[a-f0-9]{40}|[a-f0-9]{64}) (?[0-3])$/u.exec( + record8.slice(0, separator) + )?.groups; + if (!header) { + throw new Error("Native Git snapshot provider returned a malformed staged header"); + } + const relative = safeGitProjectPath(record8.slice(separator + 1)); + if (relative === null) { + throw new Error("Native Git snapshot provider returned an unsafe staged path"); + } + tracked.add(relative); + if (header.mode === "160000" && header.stage === "0") gitlinks.add(relative); + } + }; + addStagedRecords(stagedBefore.records); + addStagedRecords(stagedAfter.records); + const combinedRecords = combined.records.map(decodeGitRecord); + const combinedPaths = requireSafeGitProjectPaths(combinedRecords, "combined"); + const untracked = new Set(combinedPaths.filter((relative) => !tracked.has(relative))); + const nestedRepositories = new Set( + combinedPaths.filter( + (relative, index) => !tracked.has(relative) && combinedRecords[index].endsWith("/") + ) + ); + for (const gitlink of gitlinks) nestedRepositories.delete(gitlink); + return { + tracked, + untracked, + gitlinks, + nestedRepositories, + omissions: [], + overflow: null, + evidence: null, + initialFence: gitSelectionFence(results) + }; +} +function sameGitSelectionStream(left, right) { + return left.hash === right.hash && left.recordCount === right.recordCount && left.storedRecordCount === right.storedRecordCount && left.stdoutBytes === right.stdoutBytes && left.overflow === right.overflow; +} +function gitSelectionFenceOverflow(fence) { + return fence.stageBefore.overflow || fence.combined.overflow || fence.stageAfter.overflow; +} +function gitSelectionChanged(initial, final) { + return !sameGitSelectionStream(initial.stageBefore, initial.stageAfter) || !sameGitSelectionStream(initial.stageAfter, final.stageBefore) || !sameGitSelectionStream(initial.combined, final.combined) || !sameGitSelectionStream(final.stageBefore, final.stageAfter); +} +async function finalizeNativeGitSnapshotSelection(execution, projectRoot, limits, selection, outputChunkBytes) { + let finalResults; + try { + finalResults = await readNativeGitSelectionResults(execution, projectRoot, limits, { + ...outputChunkBytes === void 0 ? {} : { outputChunkBytes } + }); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider failed during its final selection fence", { + cause: error + }); + } + const initial = selection.initialFence; + const final = gitSelectionFence(finalResults); + const hasOverflow = gitSelectionFenceOverflow(initial) || gitSelectionFenceOverflow(final); + const changed = gitSelectionChanged(initial, final); + if (hasOverflow) { + selection.omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "git-enumeration-limit" + }); + } + if (changed) { + selection.omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "git-selection-changed" + }); + } + selection.evidence = hasOverflow || changed ? { + schema: "comet.native.git-selection.v1", + status: hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed", + ...initial, + finalStageBefore: final.stageBefore, + finalCombined: final.combined, + finalStageAfter: final.stageAfter + } : null; + if (initial.combined.overflow || final.combined.overflow) { + selection.overflow = { + count: Math.max( + 1, + initial.combined.recordCount - initial.combined.storedRecordCount, + final.combined.recordCount - final.combined.storedRecordCount + ), + hash: sha256Text( + `comet.native.git-selection-overflow.v2 +${JSON.stringify({ + initial: initial.combined, + final: final.combined + })}` + ) + }; + } +} +var PHYSICAL_SELECTION_SUM_MASK = (1n << 256n) - 1n; +function physicalSelectionRecordType(stat) { + if (stat.isFile()) return "file"; + if (stat.isDirectory()) return "directory"; + if (stat.isSymbolicLink()) return "symlink"; + return "other"; +} +async function nativePhysicalSnapshotSelection(options) { + const projectRoot = path10.resolve(options.paths.projectRoot); + const nativeRoot = path10.resolve(options.paths.nativeRoot); + const configFile = path10.resolve(options.paths.configFile); + const selectionFile = path10.join(projectRoot, ".comet", "current-change.json"); + const records = []; + const omissions = []; + const xor = Buffer.alloc(32); + let sum = 0n; + let visitedNodeCount = 0; + let recordCount = 0; + let encodedBytes = 0; + let overflow = false; + let unstable = false; + let stopped = false; + const hasExecutionBudget = () => { + if (remainingNativeSnapshotTime(options.execution) >= 1) return true; + overflow = true; + stopped = true; + return false; + }; + const addRecord = (record8) => { + const encoded = Buffer.from(`${record8.type}\0${record8.path}`, "utf8"); + const digest = createHash5("sha256").update("comet.native.physical-selection-record.v1\0").update(encoded).digest(); + for (let index = 0; index < xor.length; index += 1) xor[index] ^= digest[index]; + sum = sum + BigInt(`0x${digest.toString("hex")}`) & PHYSICAL_SELECTION_SUM_MASK; + recordCount += 1; + encodedBytes += encoded.byteLength; + if (Buffer.byteLength(record8.path, "utf8") > options.limits.maxPathBytes || encodedBytes > options.limits.maxBytes) { + overflow = true; + stopped = true; + return; + } + records.push(record8); + }; + const visit = async (directory) => { + if (stopped) return; + if (!hasExecutionBudget()) return; + let handle; + try { + handle = await fs10.opendir(directory); + } catch (error) { + if (!hasExecutionBudget()) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + if (directory === projectRoot && isUnreadableError(error)) throw error; + unstable ||= isChangedDuringReadError(error); + overflow ||= isUnreadableError(error); + stopped = true; + return; + } + if (!hasExecutionBudget()) { + try { + await handle.close(); + } catch (error) { + if (error.code !== "ERR_DIR_CLOSED") throw error; + } + return; + } + let traversalFailed = false; + let traversalError; + try { + while (!stopped) { + if (!hasExecutionBudget()) break; + let child; + try { + child = await handle.read(); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + if (directory === projectRoot && isUnreadableError(error)) throw error; + unstable ||= isChangedDuringReadError(error); + overflow ||= isUnreadableError(error); + stopped = true; + break; + } + if (!hasExecutionBudget() || child === null) break; + visitedNodeCount += 1; + if (visitedNodeCount > options.limits.maxNodes) { + overflow = true; + stopped = true; + break; + } + const target = path10.join(directory, child.name); + const relative = portableRelative(projectRoot, target); + if (target === configFile || target === selectionFile || sameOrInside(nativeRoot, target) || options.denylist.some((denied) => sameOrInside(denied, target)) || nativeSensitiveRelativePathReason(relative) !== null) { + continue; + } + if (!hasExecutionBudget()) break; + let stat; + try { + stat = await fs10.lstat(target); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + unstable = true; + addRecord({ path: relative, type: "other" }); + omissions.push({ + path: relative, + size: null, + type: "file", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + await options.hooks?.afterNode?.(relative); + if (stopped) break; + if (!hasExecutionBudget()) break; + continue; + } + if (!hasExecutionBudget()) break; + const type = physicalSelectionRecordType(stat); + if (type === "directory") { + if (!hasExecutionBudget()) break; + let realDirectory; + try { + realDirectory = await fs10.realpath(target); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + unstable = true; + addRecord({ path: relative, type }); + omissions.push({ + path: relative, + size: null, + type: "directory", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + await options.hooks?.afterNode?.(relative); + if (stopped) break; + if (!hasExecutionBudget()) break; + continue; + } + if (!hasExecutionBudget()) break; + if (!isInsidePath(options.physicalProjectRoot, realDirectory) || sameOrInside(options.physicalNativeRoot, realDirectory)) { + continue; + } + addRecord({ path: relative, type }); + await options.hooks?.afterNode?.(relative); + if (!hasExecutionBudget()) break; + await visit(target); + if (stopped) break; + continue; + } + addRecord({ path: relative, type }); + await options.hooks?.afterNode?.(relative); + if (!hasExecutionBudget()) break; + if (stopped) break; + } + } catch (error) { + traversalFailed = true; + traversalError = error; + } + let closeError; + try { + await handle.close(); + } catch (error) { + closeError = error; + } + const withinBudget = hasExecutionBudget(); + if (traversalFailed) throw traversalError; + if (withinBudget && closeError !== void 0 && closeError.code !== "ERR_DIR_CLOSED") { + throw closeError; + } + }; + await visit(projectRoot); + hasExecutionBudget(); + if (overflow) { + records.length = 0; + recordCount = 0; + encodedBytes = 0; + } else { + records.sort((left, right) => left.path.localeCompare(right.path, "en")); + omissions.sort((left, right) => left.path.localeCompare(right.path, "en")); + } + const hash6 = overflow ? sha256Text( + `comet.native.physical-selection-incomplete.v1 +${JSON.stringify({ + maxNodes: options.limits.maxNodes, + maxBytes: options.limits.maxBytes, + maxPathBytes: options.limits.maxPathBytes + })}` + ) : sha256Text( + `comet.native.physical-selection.v1 +${JSON.stringify({ + visitedNodeCount, + recordCount, + encodedBytes, + xor: xor.toString("hex"), + sum: sum.toString(16).padStart(64, "0") + })}` + ); + return { + records, + omissions, + evidence: { + hash: hash6, + visitedNodeCount, + recordCount, + storedRecordCount: records.length, + encodedBytes, + overflow, + unstable + } + }; +} +function samePhysicalSelectionStream(left, right) { + return left.hash === right.hash && left.visitedNodeCount === right.visitedNodeCount && left.recordCount === right.recordCount && left.storedRecordCount === right.storedRecordCount && left.encodedBytes === right.encodedBytes && left.overflow === right.overflow && left.unstable === right.unstable; +} +function finalizeNativePhysicalSelection(before, after) { + const hasOverflow = before.overflow || after.overflow; + const changed = before.unstable || after.unstable || !samePhysicalSelectionStream(before, after); + const omissions = []; + if (hasOverflow) { + omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "physical-enumeration-limit" + }); + } + if (changed) { + omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "physical-selection-changed" + }); + } + return { + omissions, + evidence: hasOverflow || changed ? { + schema: "comet.native.physical-selection.v1", + status: hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed", + before, + after + } : null + }; +} +function selectionPaths(selection) { + return [.../* @__PURE__ */ new Set([...selection.tracked, ...selection.untracked])].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +async function readGitlinkWorkingTreeHeadHash(execution, target) { + const output = await runGitBoundedOutput(execution, target, ["rev-parse", "--verify", "HEAD"]); + const objectId = output.toString("utf8").trim().toLowerCase(); + if (!GIT_OBJECT_ID_PATTERN.test(objectId)) { + throw new Error("Native Git snapshot provider received an invalid submodule HEAD"); + } + return sha256Text(`gitlink:${objectId}`); +} +async function inspectGitlinkWorkingTree(execution, target) { + const before = await readGitlinkWorkingTreeHeadHash(execution, target); + const dirty = await runGitHasOutput(execution, target, [ + "status", + "--porcelain=v1", + "-z", + "--untracked-files=normal" + ]); + const after = await readGitlinkWorkingTreeHeadHash(execution, target); + if (after !== before) { + throw new Error("Native Git snapshot provider observed a changing submodule HEAD"); + } + return { hash: after, dirty }; +} +function isSnapshotProjectRef(paths, relative) { + if (nativeSensitiveRelativePathReason(relative) !== null) return false; + const target = path10.resolve(paths.projectRoot, ...relative.split("/")); + return !sameOrInside(path10.resolve(paths.nativeRoot), target); +} +function inspectNativeContentSnapshotHealth(value, options = {}) { + const manifest = parseNativeContentSnapshotManifest(value); + const maxRecordedPaths = options.maxRecordedPaths ?? 20; + if (!Number.isSafeInteger(maxRecordedPaths) || maxRecordedPaths < 0) { + throw new Error("Native snapshot health maxRecordedPaths must be a non-negative integer"); + } + const samplePaths = manifest.omitted.slice(0, maxRecordedPaths).map((omission) => omission.path); + return { + complete: manifest.complete, + omittedCount: manifest.omittedCount, + recordedOmissionCount: manifest.omitted.length, + overflowCount: manifest.omissionOverflow?.count ?? 0, + samplePaths, + sampleTruncated: samplePaths.length < manifest.omittedCount + }; +} +function portableRelative(root, target) { + return path10.relative(root, target).split(path10.sep).join("/"); +} +function normalizedDenylist(projectRoot, values) { + return values.map((value) => path10.resolve(projectRoot, ...value.split(/[\\/]/u))); +} +function sameOrInside(root, target) { + const normalizedRoot = path10.resolve(root); + const normalizedTarget = path10.resolve(target); + return normalizedTarget === normalizedRoot || isInsidePath(normalizedRoot, normalizedTarget); +} +function isUnreadableError(error) { + return UNREADABLE_ERROR_CODES.has(error.code ?? ""); +} +function isChangedDuringReadError(error) { + return error.code === "ENOENT"; +} +function serializedManifestBytes(manifest) { + return Buffer.byteLength(JSON.stringify(manifest, null, 2) + "\n"); +} +function snapshotPolicyHash(include, exclude) { + return sha256Text( + `comet.native.snapshot-policy.v1 +${JSON.stringify({ include, exclude, hash: "sha256" })}` + ); +} +function epsilonClosure(tokens, positions, checkpoint) { + const closure = new Set(positions); + const pending = [...positions]; + while (pending.length > 0) { + if (checkpoint && !checkpoint()) return null; + const position = pending.pop(); + const token = tokens[position]; + if (token && (token.kind === "star" || token.kind === "globstar" || token.kind === "globstar-slash") && !closure.has(position + 1)) { + closure.add(position + 1); + pending.push(position + 1); + } + } + return closure; +} +function cooperativePatternCheckpoint(hasBudget) { + let operationsUntilCheck = 0; + return () => { + if (operationsUntilCheck > 0) { + operationsUntilCheck -= 1; + return true; + } + if (!hasBudget()) return false; + operationsUntilCheck = 63; + return true; + }; +} +function compileNativeSnapshotPattern(pattern) { + const normalized = normalizeNativeSnapshotPattern(pattern, "Native snapshot pattern"); + const tokens = []; + for (let index = 0; index < normalized.length; index += 1) { + const character = normalized[index]; + if (character === "*" && normalized[index + 1] === "*") { + index += 1; + if (normalized[index + 1] === "/") { + index += 1; + tokens.push({ kind: "globstar-slash" }); + } else { + tokens.push({ kind: "globstar" }); + } + } else if (character === "*") { + tokens.push({ kind: "star" }); + } else if (character === "?") { + tokens.push({ kind: "question" }); + } else { + tokens.push({ kind: "literal", value: character }); + } + } + return (relative, hasBudget) => { + const checkpoint = hasBudget ? cooperativePatternCheckpoint(hasBudget) : void 0; + if (checkpoint && !checkpoint()) return false; + let positions = epsilonClosure(tokens, /* @__PURE__ */ new Set([0]), checkpoint); + if (positions === null) return false; + for (const character of relative) { + if (checkpoint && !checkpoint()) return false; + const next = /* @__PURE__ */ new Set(); + for (const position of positions) { + if (checkpoint && !checkpoint()) return false; + const token = tokens[position]; + if (!token) continue; + if (token.kind === "literal" && token.value === character) { + next.add(position + 1); + } else if (token.kind === "question" && character !== "/") { + next.add(position + 1); + } else if (token.kind === "star" && character !== "/") { + next.add(position); + } else if (token.kind === "globstar") { + next.add(position); + } else if (token.kind === "globstar-slash") { + next.add(position); + if (character === "/") next.add(position + 1); + } + } + positions = epsilonClosure(tokens, next, checkpoint); + if (positions === null) return false; + if (positions.size === 0) return false; + } + return epsilonClosure(tokens, positions, checkpoint)?.has(tokens.length) ?? false; + }; +} +function resolveSnapshotPolicy(value) { + if (value === void 0) return void 0; + const include = [ + ...new Set(value.include.map((item) => normalizeNativeSnapshotPattern(item, "include"))) + ].sort((left, right) => left.localeCompare(right, "en")); + const exclude = [ + ...new Set(value.exclude.map((item) => normalizeNativeSnapshotPattern(item, "exclude"))) + ].sort((left, right) => left.localeCompare(right, "en")); + if (include.length === 0) throw new Error("Native snapshot policy include must not be empty"); + const hash6 = snapshotPolicyHash(include, exclude); + if ("hash" in value && value.hash !== hash6) { + throw new Error("Native snapshot policy hash is invalid"); + } + return { + manifest: { + schema: "comet.native.snapshot-policy.v1", + include, + exclude, + hash: hash6 + }, + includeMatchers: include.map(compileNativeSnapshotPattern), + excludeMatchers: exclude.map(compileNativeSnapshotPattern) + }; +} +function snapshotPolicyIncludes(policy, relative, execution) { + if (!policy) return true; + const hasBudget = () => nativeSnapshotExecutionHasBudget(execution); + let included = false; + for (const matcher of policy.includeMatchers) { + if (!hasBudget()) return false; + if (matcher(relative, hasBudget)) { + included = true; + break; + } + } + if (!included) return false; + for (const matcher of policy.excludeMatchers) { + if (!hasBudget()) return false; + if (matcher(relative, hasBudget)) return false; + } + return true; +} +function foldSnapshotOverflowHash(previous, kind, value) { + const payload = JSON.stringify(value); + return sha256Text( + `${previous} +${Buffer.byteLength(kind)}:${kind} +${Buffer.byteLength(payload)}:${payload}` + ); +} +function isSelectionIntegrityOmission(omission) { + return omission.reason === "git-enumeration-limit" || omission.reason === "git-selection-changed" || omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed"; +} +function takeLastCompactableOmission(omissions) { + for (let index = omissions.length - 1; index >= 0; index -= 1) { + const omission = omissions[index]; + if (isSelectionIntegrityOmission(omission)) continue; + omissions.splice(index, 1); + return omission; + } + return null; +} +function sameFileIdentity4(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function sha256FileBounded(file, maxBytes, expected, execution) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + let handle; + try { + handle = await fs10.open(file, "r"); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + throw error; + } + if (!nativeSnapshotExecutionHasBudget(execution)) { + await handle.close(); + return { status: "budget-exhausted" }; + } + const hash6 = createHash5("sha256"); + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + let bytes = 0; + try { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const opened = await handle.stat(); + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + if (!opened.isFile() || !sameFileIdentity4(expected, opened)) return { status: "changed" }; + while (true) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const remaining = maxBytes + 1 - bytes; + if (remaining < 1) return { status: "changed" }; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + if (result2.bytesRead === 0) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const finalStat = await handle.stat(); + if (!nativeSnapshotExecutionHasBudget(execution)) { + return { status: "budget-exhausted" }; + } + if (!finalStat.isFile() || !sameFileIdentity4(opened, finalStat)) { + return { status: "changed" }; + } + return { status: "complete", hash: hash6.digest("hex"), bytes, finalStat }; + } + if (bytes + result2.bytesRead > maxBytes) return { status: "changed" }; + hash6.update(buffer.subarray(0, result2.bytesRead)); + bytes += result2.bytesRead; + } + } finally { + await handle.close(); + } +} +function record3(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function rejectUnknown3(value, keys, label) { + const unknown = Object.keys(value).find((key) => !keys.has(key)); + if (unknown) throw new Error(`${label} contains unknown field: ${unknown}`); +} +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function nonNegativeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function snapshotPath(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + const normalized = path10.posix.normalize(value); + if (normalized !== value || path10.posix.isAbsolute(value) || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function parseEntry(value, index) { + const entry2 = record3(value, `Native snapshot entry ${index}`); + rejectUnknown3(entry2, ENTRY_KEYS, `Native snapshot entry ${index}`); + const entryPath = snapshotPath(entry2.path, `Native snapshot entry ${index} path`); + if (typeof entry2.hash !== "string" || !HASH_PATTERN.test(entry2.hash)) { + throw new Error(`Native snapshot entry ${index} hash is invalid`); + } + if (entry2.type !== "file") throw new Error(`Native snapshot entry ${index} type is invalid`); + return { + path: entryPath, + hash: entry2.hash, + size: nonNegativeInteger(entry2.size, `Native snapshot entry ${index} size`), + type: "file" + }; +} +function parseOmission(value, index) { + const omission = record3(value, `Native snapshot omission ${index}`); + rejectUnknown3(omission, OMISSION_KEYS, `Native snapshot omission ${index}`); + if (!OMISSION_TYPES.has(omission.type)) { + throw new Error(`Native snapshot omission ${index} type is invalid`); + } + if (!OMISSION_REASONS.has(omission.reason)) { + throw new Error(`Native snapshot omission ${index} reason is invalid`); + } + return { + path: snapshotPath(omission.path, `Native snapshot omission ${index} path`), + size: omission.size === null ? null : nonNegativeInteger(omission.size, `Native snapshot omission ${index} size`), + type: omission.type, + reason: omission.reason + }; +} +function parseOmissionOverflow(value) { + const overflow = record3(value, "Native snapshot omission overflow"); + rejectUnknown3(overflow, OMISSION_OVERFLOW_KEYS, "Native snapshot omission overflow"); + if (typeof overflow.hash !== "string" || !HASH_PATTERN.test(overflow.hash)) { + throw new Error("Native snapshot omission overflow hash is invalid"); + } + const expectedRef = `native-snapshot://omitted-overflow/${overflow.hash}`; + if (overflow.ref !== expectedRef) { + throw new Error("Native snapshot omission overflow ref is invalid"); + } + return { + ref: expectedRef, + hash: overflow.hash, + count: positiveInteger(overflow.count, "Native snapshot omission overflow count") + }; +} +function parseGitSelectionStreamEvidence(value, label) { + const stream = record3(value, label); + rejectUnknown3(stream, GIT_SELECTION_STREAM_KEYS, label); + if (typeof stream.hash !== "string" || !HASH_PATTERN.test(stream.hash)) { + throw new Error(`${label} hash is invalid`); + } + if (typeof stream.overflow !== "boolean") { + throw new Error(`${label} overflow flag is invalid`); + } + const recordCount = nonNegativeInteger(stream.recordCount, `${label} recordCount`); + const storedRecordCount = nonNegativeInteger( + stream.storedRecordCount, + `${label} storedRecordCount` + ); + const stdoutBytes = nonNegativeInteger(stream.stdoutBytes, `${label} stdoutBytes`); + if (storedRecordCount > recordCount || !stream.overflow && storedRecordCount !== recordCount) { + throw new Error(`${label} stored record count is inconsistent`); + } + return { + hash: stream.hash, + recordCount, + storedRecordCount, + stdoutBytes, + overflow: stream.overflow + }; +} +function parseGitSelectionEvidence(value) { + const selection = record3(value, "Native Git selection evidence"); + rejectUnknown3(selection, GIT_SELECTION_KEYS, "Native Git selection evidence"); + if (selection.schema !== "comet.native.git-selection.v1") { + throw new Error("Native Git selection evidence schema is invalid"); + } + if (selection.status !== "overflow" && selection.status !== "changed" && selection.status !== "overflow-and-changed") { + throw new Error("Native Git selection evidence status is invalid"); + } + const stageBefore = parseGitSelectionStreamEvidence( + selection.stageBefore, + "Native Git selection stageBefore" + ); + const combined = parseGitSelectionStreamEvidence( + selection.combined, + "Native Git selection combined" + ); + const stageAfter = parseGitSelectionStreamEvidence( + selection.stageAfter, + "Native Git selection stageAfter" + ); + const finalStageBefore = parseGitSelectionStreamEvidence( + selection.finalStageBefore, + "Native Git selection finalStageBefore" + ); + const finalCombined = parseGitSelectionStreamEvidence( + selection.finalCombined, + "Native Git selection finalCombined" + ); + const finalStageAfter = parseGitSelectionStreamEvidence( + selection.finalStageAfter, + "Native Git selection finalStageAfter" + ); + const hasOverflow = [ + stageBefore, + combined, + stageAfter, + finalStageBefore, + finalCombined, + finalStageAfter + ].some((stream) => stream.overflow); + const changed = !sameGitSelectionStream(stageBefore, stageAfter) || !sameGitSelectionStream(stageAfter, finalStageBefore) || !sameGitSelectionStream(combined, finalCombined) || !sameGitSelectionStream(finalStageBefore, finalStageAfter); + const expectedStatus = hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed"; + if (!hasOverflow && !changed) { + throw new Error("Native Git selection evidence must describe an exceptional selection"); + } + if (selection.status !== expectedStatus) { + throw new Error("Native Git selection evidence status is inconsistent"); + } + return { + schema: "comet.native.git-selection.v1", + status: expectedStatus, + stageBefore, + combined, + stageAfter, + finalStageBefore, + finalCombined, + finalStageAfter + }; +} +function parsePhysicalSelectionStreamEvidence(value, label) { + const stream = record3(value, label); + rejectUnknown3(stream, PHYSICAL_SELECTION_STREAM_KEYS, label); + if (typeof stream.hash !== "string" || !HASH_PATTERN.test(stream.hash)) { + throw new Error(`${label} hash is invalid`); + } + if (typeof stream.overflow !== "boolean" || typeof stream.unstable !== "boolean") { + throw new Error(`${label} flags are invalid`); + } + const visitedNodeCount = nonNegativeInteger(stream.visitedNodeCount, `${label} visitedNodeCount`); + const recordCount = nonNegativeInteger(stream.recordCount, `${label} recordCount`); + const storedRecordCount = nonNegativeInteger( + stream.storedRecordCount, + `${label} storedRecordCount` + ); + const encodedBytes = nonNegativeInteger(stream.encodedBytes, `${label} encodedBytes`); + if (storedRecordCount > recordCount || !stream.overflow && storedRecordCount !== recordCount) { + throw new Error(`${label} stored record count is inconsistent`); + } + return { + hash: stream.hash, + visitedNodeCount, + recordCount, + storedRecordCount, + encodedBytes, + overflow: stream.overflow, + unstable: stream.unstable + }; +} +function parsePhysicalSelectionEvidence(value) { + const selection = record3(value, "Native physical selection evidence"); + rejectUnknown3(selection, PHYSICAL_SELECTION_KEYS, "Native physical selection evidence"); + if (selection.schema !== "comet.native.physical-selection.v1") { + throw new Error("Native physical selection evidence schema is invalid"); + } + if (selection.status !== "overflow" && selection.status !== "changed" && selection.status !== "overflow-and-changed") { + throw new Error("Native physical selection evidence status is invalid"); + } + const before = parsePhysicalSelectionStreamEvidence( + selection.before, + "Native physical selection before" + ); + const after = parsePhysicalSelectionStreamEvidence( + selection.after, + "Native physical selection after" + ); + const hasOverflow = before.overflow || after.overflow; + const changed = before.unstable || after.unstable || !samePhysicalSelectionStream(before, after); + if (!hasOverflow && !changed) { + throw new Error("Native physical selection evidence must describe an exceptional selection"); + } + const expectedStatus = hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed"; + if (selection.status !== expectedStatus) { + throw new Error("Native physical selection evidence status is inconsistent"); + } + return { + schema: "comet.native.physical-selection.v1", + status: expectedStatus, + before, + after + }; +} +function parseNativeContentSnapshotManifest(value) { + const manifest = record3(value, "Native content snapshot manifest"); + rejectUnknown3(manifest, MANIFEST_KEYS, "Native content snapshot manifest"); + if (manifest.schema !== "comet.native.content-snapshot.v1") { + throw new Error("Unsupported Native content snapshot schema"); + } + if (!SNAPSHOT_ORIGINS.has(manifest.origin)) { + throw new Error("Native content snapshot origin is invalid"); + } + let capture; + if (manifest.capture !== void 0) { + const captureValue = record3(manifest.capture, "Native content snapshot capture"); + rejectUnknown3(captureValue, CAPTURE_KEYS, "Native content snapshot capture"); + if (captureValue.provider !== "git" && captureValue.provider !== "physical-tree") { + throw new Error("Native content snapshot capture provider is invalid"); + } + const gitSelection2 = captureValue.gitSelection === void 0 ? void 0 : parseGitSelectionEvidence(captureValue.gitSelection); + const physicalSelection2 = captureValue.physicalSelection === void 0 ? void 0 : parsePhysicalSelectionEvidence(captureValue.physicalSelection); + let projection = null; + if (captureValue.projection !== void 0) { + const projectionValue = record3(captureValue.projection, "Native content snapshot projection"); + rejectUnknown3(projectionValue, GIT_PROJECTION_KEYS, "Native content snapshot projection"); + if (projectionValue.provider !== "git") { + throw new Error("Native content snapshot projection provider is invalid"); + } + projection = { + provider: "git", + ...projectionValue.selection === void 0 ? {} : { selection: parseGitSelectionEvidence(projectionValue.selection) } + }; + } + if (captureValue.provider === "git") { + if (physicalSelection2 || projection) { + throw new Error("Native Git capture cannot include physical or projection evidence"); + } + capture = { + provider: "git", + ...gitSelection2 ? { gitSelection: gitSelection2 } : {} + }; + } else { + if (gitSelection2) { + throw new Error("Native physical-tree capture cannot include direct Git evidence"); + } + if (physicalSelection2 && projection) { + throw new Error("Native physical-tree capture cannot combine selection and projection"); + } + capture = projection ? { provider: "physical-tree", projection } : { + provider: "physical-tree", + ...physicalSelection2 ? { physicalSelection: physicalSelection2 } : {} + }; + } + } + if (typeof manifest.createdAt !== "string" || Number.isNaN(Date.parse(manifest.createdAt))) { + throw new Error("Native content snapshot timestamp is invalid"); + } + if (typeof manifest.complete !== "boolean") { + throw new Error("Native content snapshot complete flag is invalid"); + } + const limitValue = record3(manifest.limits, "Native content snapshot limits"); + rejectUnknown3(limitValue, LIMIT_KEYS, "Native content snapshot limits"); + const limits = { + maxFiles: positiveInteger(limitValue.maxFiles, "Native snapshot maxFiles"), + maxFileBytes: positiveInteger(limitValue.maxFileBytes, "Native snapshot maxFileBytes"), + maxTotalBytes: positiveInteger(limitValue.maxTotalBytes, "Native snapshot maxTotalBytes"), + maxManifestBytes: positiveInteger( + limitValue.maxManifestBytes, + "Native snapshot maxManifestBytes" + ), + ...limitValue.maxDurationMs === void 0 ? {} : { + maxDurationMs: positiveInteger(limitValue.maxDurationMs, "Native snapshot maxDurationMs") + } + }; + let policy; + if (manifest.policy !== void 0) { + const policyValue = record3(manifest.policy, "Native snapshot policy"); + rejectUnknown3(policyValue, POLICY_KEYS, "Native snapshot policy"); + if (policyValue.schema !== "comet.native.snapshot-policy.v1") { + throw new Error("Native snapshot policy schema is invalid"); + } + if (!Array.isArray(policyValue.include) || !Array.isArray(policyValue.exclude)) { + throw new Error("Native snapshot policy patterns must be arrays"); + } + policy = resolveSnapshotPolicy({ + include: policyValue.include, + exclude: policyValue.exclude, + hash: policyValue.hash, + schema: "comet.native.snapshot-policy.v1" + }).manifest; + } + if (!Array.isArray(manifest.entries) || !Array.isArray(manifest.omitted)) { + throw new Error("Native content snapshot entries and omissions must be arrays"); + } + const entries = manifest.entries.map(parseEntry); + const omitted = manifest.omitted.map(parseOmission); + const omittedCount = nonNegativeInteger( + manifest.omittedCount, + "Native content snapshot omittedCount" + ); + const omissionOverflow = manifest.omissionOverflow === void 0 ? void 0 : parseOmissionOverflow(manifest.omissionOverflow); + if (entries.length > limits.maxFiles) { + throw new Error("Native content snapshot exceeds its file-count limit"); + } + if (entries.some((entry2) => entry2.size > limits.maxFileBytes) || entries.reduce((total, entry2) => total + entry2.size, 0) > limits.maxTotalBytes) { + throw new Error("Native content snapshot exceeds its byte limits"); + } + if (new Set(entries.map((entry2) => entry2.path)).size !== entries.length) { + throw new Error("Native content snapshot contains duplicate paths"); + } + if (omitted.length > MAX_RECORDED_OMISSIONS || omittedCount < omitted.length) { + throw new Error("Native content snapshot omission count is invalid"); + } + const overflowCount = omittedCount - omitted.length; + if (overflowCount === 0 && omissionOverflow || overflowCount > 0 && omissionOverflow?.count !== overflowCount) { + throw new Error("Native content snapshot omission overflow is inconsistent"); + } + if (manifest.complete !== (omittedCount === 0)) { + throw new Error("Native content snapshot completeness is inconsistent"); + } + const enumerationOmissions = omitted.filter( + (omission) => omission.reason === "git-enumeration-limit" + ); + const selectionChangedOmissions = omitted.filter( + (omission) => omission.reason === "git-selection-changed" + ); + for (const omission of [...enumerationOmissions, ...selectionChangedOmissions]) { + if (omission.path !== "." || omission.size !== null || omission.type !== "directory") { + throw new Error("Native Git selection omission must use the project-root sentinel"); + } + } + if (enumerationOmissions.length > 1 || selectionChangedOmissions.length > 1) { + throw new Error("Native Git selection omissions must not be duplicated"); + } + const gitSelection = capture?.provider === "git" ? capture.gitSelection : capture?.projection?.selection; + const evidenceHasOverflow = gitSelection?.status === "overflow" || gitSelection?.status === "overflow-and-changed"; + const evidenceHasSelectionChange = gitSelection?.status === "changed" || gitSelection?.status === "overflow-and-changed"; + if (evidenceHasOverflow !== (enumerationOmissions.length === 1)) { + throw new Error("Native Git enumeration omission and selection evidence are inconsistent"); + } + if (evidenceHasSelectionChange !== (selectionChangedOmissions.length === 1)) { + throw new Error("Native Git selection-change omission and selection evidence are inconsistent"); + } + const physicalEnumerationOmissions = omitted.filter( + (omission) => omission.reason === "physical-enumeration-limit" + ); + const physicalChangedOmissions = omitted.filter( + (omission) => omission.reason === "physical-selection-changed" + ); + for (const omission of [...physicalEnumerationOmissions, ...physicalChangedOmissions]) { + if (omission.path !== "." || omission.size !== null || omission.type !== "directory") { + throw new Error("Native physical selection omission must use the project-root sentinel"); + } + } + if (physicalEnumerationOmissions.length > 1 || physicalChangedOmissions.length > 1) { + throw new Error("Native physical selection omissions must not be duplicated"); + } + const physicalSelection = capture?.physicalSelection; + const physicalEvidenceHasOverflow = physicalSelection?.status === "overflow" || physicalSelection?.status === "overflow-and-changed"; + const physicalEvidenceHasChange = physicalSelection?.status === "changed" || physicalSelection?.status === "overflow-and-changed"; + if (physicalEvidenceHasOverflow !== (physicalEnumerationOmissions.length === 1)) { + throw new Error("Native physical enumeration omission and evidence are inconsistent"); + } + if (physicalEvidenceHasChange !== (physicalChangedOmissions.length === 1)) { + throw new Error("Native physical selection-change omission and evidence are inconsistent"); + } + const parsed = { + schema: "comet.native.content-snapshot.v1", + origin: manifest.origin, + ...capture ? { capture } : {}, + createdAt: manifest.createdAt, + complete: manifest.complete, + limits, + ...policy ? { policy } : {}, + entries, + omitted, + omittedCount, + ...omissionOverflow ? { omissionOverflow } : {} + }; + if (serializedManifestBytes(parsed) > limits.maxManifestBytes) { + throw new Error("Native content snapshot exceeds its manifest byte limit"); + } + return parsed; +} +async function filterNativeContentSnapshotToProjectScope(paths, value, options = {}) { + const manifest = parseNativeContentSnapshotManifest(value); + if (manifest.capture?.provider === "git" || manifest.capture?.projection || manifest.capture?.physicalSelection) { + return manifest; + } + const projectRoot = path10.resolve(paths.projectRoot); + const execution = createNativeSnapshotExecution(options); + const gitSelectionLimits = resolveNativeGitSelectionLimits(options.gitSelectionLimits); + const selection = await nativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + options.gitSelectionHooks + ); + if (selection === null) return manifest; + const selected = new Set( + selectionPaths(selection).filter((relative) => isSnapshotProjectRef(paths, relative)) + ); + const gitlinkPaths = [...selection.gitlinks].filter((relative) => selected.has(relative)); + const nestedRepositoryPaths = [...selection.nestedRepositories].filter( + (relative) => isSnapshotProjectRef(paths, relative) + ); + const atOrBeneath = (relative, boundaries) => boundaries.some((boundary) => relative === boundary || relative.startsWith(`${boundary}/`)); + const beneathGitlink = (relative) => atOrBeneath(relative, gitlinkPaths); + const beneathNestedRepository = (relative) => atOrBeneath(relative, nestedRepositoryPaths); + const ignoredCandidates = [ + ...manifest.entries.map((entry2) => entry2.path), + ...manifest.omitted.flatMap( + (omission) => omission.type === "directory" ? [omission.path, `${omission.path}/`] : [omission.path] + ) + ].filter( + (relative) => !selected.has(relative) && !beneathGitlink(relative) && !beneathNestedRepository(relative) + ); + const ignored = await runGitCheckIgnore(execution, projectRoot, ignoredCandidates); + const entries = manifest.entries.filter( + (entry2) => !beneathGitlink(entry2.path) && !beneathNestedRepository(entry2.path) && isSnapshotProjectRef(paths, entry2.path) && (selected.has(entry2.path) || !ignored.has(entry2.path)) + ); + await finalizeNativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + selection, + options.gitSelectionHooks?.outputChunkBytes + ); + const boundaryOmissions = [...selection.omissions]; + for (const gitlink of gitlinkPaths) { + boundaryOmissions.push({ + path: gitlink, + size: null, + type: "directory", + reason: "legacy-gitlink-boundary" + }); + } + entries.sort((left, right) => left.path.localeCompare(right.path, "en")); + const omitted = manifest.omitted.filter((omission) => { + if (beneathGitlink(omission.path)) return false; + if (beneathNestedRepository(omission.path)) return false; + if (!isSnapshotProjectRef(paths, omission.path)) return false; + if (!ignored.has(omission.path)) return true; + return selected.has(omission.path) || [...selected].some((relative) => relative.startsWith(`${omission.path}/`)); + }); + for (const omission of boundaryOmissions) { + if (!omitted.some( + (current) => current.path === omission.path && current.reason === omission.reason + )) { + omitted.push(omission); + } + } + omitted.sort((left, right) => left.path.localeCompare(right.path, "en")); + let overflowCount = manifest.omissionOverflow?.count ?? 0; + let overflowHash = manifest.omissionOverflow?.hash ?? sha256Text("comet.native.snapshot-omission-overflow.v1"); + if (selection.overflow) { + overflowCount += selection.overflow.count; + overflowHash = foldSnapshotOverflowHash(overflowHash, "git-selection", { + source: "git-selection", + ...selection.overflow + }); + } + const foldOverflow = (omission) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "omission", omission); + }; + while (omitted.length > MAX_RECORDED_OMISSIONS) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) { + throw new Error( + "Projected Native snapshot has too many required selection-integrity omissions" + ); + } + foldOverflow(omission); + } + const buildProjection = () => ({ + ...manifest, + capture: { + provider: "physical-tree", + projection: { + provider: "git", + ...selection.evidence ? { selection: selection.evidence } : {} + } + }, + complete: omitted.length + overflowCount === 0, + entries, + omitted, + omittedCount: omitted.length + overflowCount, + ...overflowCount > 0 ? { + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${overflowHash}`, + hash: overflowHash, + count: overflowCount + } + } : {} + }); + let projected = buildProjection(); + while (serializedManifestBytes(projected) > manifest.limits.maxManifestBytes) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) { + throw new Error("Projected Native snapshot cannot fit its manifest byte limit"); + } + foldOverflow(omission); + projected = buildProjection(); + } + return parseNativeContentSnapshotManifest(projected); +} +function nativeBaselineManifestFile(paths, name) { + if (!CHANGE_NAME_PATTERN.test(name)) throw new Error(`Invalid Native change name: ${name}`); + const changeDir = path10.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, changeDir)) throw new Error("Native change path escaped"); + return path10.join(changeDir, "runtime", "baseline-manifest.json"); +} +async function createNativeContentSnapshot(paths, options = {}) { + const limits = { + maxFiles: options.limits?.maxFiles ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxFiles, + maxFileBytes: options.limits?.maxFileBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxFileBytes, + maxTotalBytes: options.limits?.maxTotalBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxTotalBytes, + maxManifestBytes: options.limits?.maxManifestBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxManifestBytes, + ...options.limits?.maxDurationMs === void 0 ? {} : { maxDurationMs: options.limits.maxDurationMs } + }; + const policy = resolveSnapshotPolicy(options.policy); + const execution = createNativeSnapshotExecution({ + ...options, + deadlineMs: options.deadlineMs ?? limits.maxDurationMs + }); + const gitSelectionLimits = resolveNativeGitSelectionLimits(options.gitSelectionLimits); + const physicalSelectionLimits = resolveNativePhysicalSelectionLimits( + options.physicalSelectionLimits + ); + if (limits.maxFiles < 1 || limits.maxFileBytes < 1 || limits.maxTotalBytes < 1 || limits.maxManifestBytes < 1) { + throw new Error("Native snapshot limits must be positive"); + } + const projectRoot = path10.resolve(paths.projectRoot); + const physicalProjectRoot = await fs10.realpath(projectRoot); + const nativeRoot = path10.resolve(paths.nativeRoot); + const physicalNativeRoot = await fs10.realpath(nativeRoot); + const configFile = path10.resolve(paths.configFile); + const selectionFile = path10.join(projectRoot, ".comet", "current-change.json"); + const denylist = normalizedDenylist(projectRoot, options.denylist ?? []); + const entries = []; + const omitted = []; + const capturedEntryValidations = /* @__PURE__ */ new Map(); + const capturedTrackedAbsences = /* @__PURE__ */ new Map(); + let omittedCount = 0; + let overflowCount = 0; + let overflowHash = sha256Text("comet.native.snapshot-omission-overflow.v1"); + let totalBytes = 0; + let notifiedFirstEntry = false; + let physicalSelectionEvidence = null; + const foldOverflow = (value) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "omission", value); + }; + const omit = (value) => { + omittedCount += 1; + if (omitted.length < MAX_RECORDED_OMISSIONS) { + omitted.push(value); + return; + } + if (isSelectionIntegrityOmission(value)) { + const displaced = takeLastCompactableOmission(omitted); + if (displaced === null) { + throw new Error("Native snapshot has too many required selection-integrity omissions"); + } + foldOverflow(displaced); + omitted.push(value); + return; + } + foldOverflow(value); + }; + const foldGitSelectionOverflow = (value) => { + omittedCount += value.count; + overflowCount += value.count; + overflowHash = foldSnapshotOverflowHash(overflowHash, "git-selection", { + source: "git-selection", + ...value + }); + }; + const foldManifestEntryOverflow = (entry2) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "manifest-entry", { + reason: "manifest-size", + entry: entry2 + }); + }; + const recordCapturedEntry = async (entry2, validation2) => { + entries.push(entry2); + totalBytes += entry2.size; + capturedEntryValidations.set(entry2.path, validation2); + if (!notifiedFirstEntry) { + notifiedFirstEntry = true; + await options.gitSelectionHooks?.afterFirstEntryCaptured?.(entry2.path); + } + }; + const invalidateCapturedEntry = (relative, omission) => { + const index = entries.findIndex((entry3) => entry3.path === relative); + if (index < 0) return; + const [entry2] = entries.splice(index, 1); + totalBytes -= entry2.size; + capturedEntryValidations.delete(relative); + omit(omission); + }; + const captureFile = async (target, relative, before) => { + if (!before.isFile() || before.isSymbolicLink()) return; + if (!nativeSnapshotExecutionHasBudget(execution)) return; + let realTarget; + try { + realTarget = await fs10.realpath(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: before.size, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isInsidePath(physicalProjectRoot, realTarget) || sameOrInside(physicalNativeRoot, realTarget)) { + return; + } + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size: before.size, type: "file", reason: "file-count" }); + return; + } + if (before.size > limits.maxFileBytes) { + omit({ path: relative, size: before.size, type: "file", reason: "file-size" }); + return; + } + if (totalBytes + before.size > limits.maxTotalBytes) { + omit({ path: relative, size: before.size, type: "file", reason: "total-size" }); + return; + } + let boundedHash; + let after; + let afterRealTarget; + try { + boundedHash = await sha256FileBounded(realTarget, before.size, before, execution); + if (boundedHash.status === "budget-exhausted" || !nativeSnapshotExecutionHasBudget(execution)) { + return; + } + if (boundedHash.status === "changed") { + omit({ path: relative, size: null, type: "file", reason: "changed-during-read" }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + afterRealTarget = await fs10.realpath(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + after = await fs10.lstat(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: before.size, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (boundedHash.bytes !== before.size || afterRealTarget !== realTarget || !sameFileIdentity4(before, boundedHash.finalStat) || !sameFileIdentity4(boundedHash.finalStat, after) || !after.isFile() || after.isSymbolicLink() || after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) { + omit({ + path: relative, + size: after.isFile() ? after.size : null, + type: after.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + await recordCapturedEntry( + { path: relative, hash: boundedHash.hash, size: after.size, type: "file" }, + { kind: "file", target, realTarget, stat: after } + ); + }; + const captureSymbolicLink = async (target, relative, before) => { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + let firstTarget; + let secondTarget; + let after; + try { + firstTarget = await fs10.readlink(target, { encoding: "buffer" }); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + after = await fs10.lstat(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + secondTarget = await fs10.readlink(target, { encoding: "buffer" }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: null, + type: "other", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!after.isSymbolicLink() || !sameFileIdentity4(before, after) || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs || !firstTarget.equals(secondTarget)) { + omit({ path: relative, size: null, type: "other", reason: "changed-during-read" }); + return; + } + const size = firstTarget.byteLength; + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size, type: "other", reason: "file-count" }); + return; + } + if (size > limits.maxFileBytes) { + omit({ path: relative, size, type: "other", reason: "file-size" }); + return; + } + if (totalBytes + size > limits.maxTotalBytes) { + omit({ path: relative, size, type: "other", reason: "total-size" }); + return; + } + const hash6 = createHash5("sha256").update("symlink\0").update(firstTarget).digest("hex"); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + await recordCapturedEntry( + { path: relative, hash: hash6, size, type: "file" }, + { kind: "symlink", target, rawTarget: firstTarget, stat: after } + ); + }; + const revalidateCapturedEntries = async () => { + for (const [relative, validation2] of [...capturedEntryValidations]) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (validation2.kind === "file") { + let realTarget2; + let stat2; + try { + realTarget2 = await fs10.realpath(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + stat2 = await fs10.lstat(validation2.target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "file", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (realTarget2 !== validation2.realTarget || !stat2.isFile() || stat2.isSymbolicLink() || !sameFileIdentity4(validation2.stat, stat2) || stat2.size !== validation2.stat.size || stat2.mtimeMs !== validation2.stat.mtimeMs || stat2.ctimeMs !== validation2.stat.ctimeMs) { + invalidateCapturedEntry(relative, { + path: relative, + size: stat2.isFile() ? stat2.size : null, + type: stat2.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + } + continue; + } + if (validation2.kind === "symlink") { + let stat2; + let rawTarget; + try { + stat2 = await fs10.lstat(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + rawTarget = await fs10.readlink(validation2.target, { encoding: "buffer" }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "other", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!stat2.isSymbolicLink() || !sameFileIdentity4(validation2.stat, stat2) || stat2.mtimeMs !== validation2.stat.mtimeMs || stat2.ctimeMs !== validation2.stat.ctimeMs || !validation2.rawTarget.equals(rawTarget)) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "other", + reason: "changed-during-read" + }); + } + continue; + } + let realTarget; + let stat; + try { + realTarget = await fs10.realpath(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + stat = await fs10.lstat(validation2.target); + } catch { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + const validBoundary = realTarget === validation2.realTarget && stat.isDirectory() && !stat.isSymbolicLink() && sameFileIdentity4(validation2.stat, stat) && isInsidePath(physicalProjectRoot, realTarget) && !sameOrInside(physicalNativeRoot, realTarget); + if (!validBoundary) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-changed" + }); + continue; + } + let workingTree; + try { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + workingTree = await inspectGitlinkWorkingTree(execution, realTarget); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (workingTree.dirty) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-dirty" + }); + } else if (workingTree.hash !== validation2.hash) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-changed" + }); + } + } + for (const [relative, target] of capturedTrackedAbsences) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + try { + const stat = await fs10.lstat(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + omit({ + path: relative, + size: stat.isFile() ? stat.size : null, + type: stat.isFile() ? "file" : stat.isDirectory() ? "directory" : "other", + reason: "changed-during-read" + }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (isChangedDuringReadError(error)) continue; + if (!isUnreadableError(error)) throw error; + omit({ path: relative, size: null, type: "file", reason: "unreadable" }); + } + } + }; + const gitSelection = await nativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + options.gitSelectionHooks + ); + if (gitSelection === null) { + const before = await nativePhysicalSnapshotSelection({ + execution, + paths, + physicalProjectRoot, + physicalNativeRoot, + denylist, + limits: physicalSelectionLimits, + hooks: options.physicalSelectionHooks + }); + await options.physicalSelectionHooks?.afterInitialSelection?.(); + for (const record8 of before.records) { + if (record8.type !== "file" && record8.type !== "symlink") continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + if (!snapshotPolicyIncludes(policy, record8.path, execution)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + const target = path10.resolve(projectRoot, ...record8.path.split("/")); + let stat; + try { + stat = await fs10.lstat(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: record8.path, + size: null, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + continue; + } + if (remainingNativeSnapshotTime(execution) < 1) break; + if (record8.type === "symlink" && stat.isSymbolicLink()) { + await captureSymbolicLink(target, record8.path, stat); + if (remainingNativeSnapshotTime(execution) < 1) break; + continue; + } + if (record8.type !== "file" || !stat.isFile() || stat.isSymbolicLink()) { + omit({ + path: record8.path, + size: stat.isFile() ? stat.size : null, + type: stat.isDirectory() ? "directory" : stat.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + continue; + } + await captureFile(target, record8.path, stat); + if (remainingNativeSnapshotTime(execution) < 1) break; + } + if (remainingNativeSnapshotTime(execution) >= 1) await revalidateCapturedEntries(); + const after = await nativePhysicalSnapshotSelection({ + execution, + paths, + physicalProjectRoot, + physicalNativeRoot, + denylist, + limits: physicalSelectionLimits + }); + const finalized = finalizeNativePhysicalSelection(before.evidence, after.evidence); + physicalSelectionEvidence = finalized.evidence; + for (const omission of [...before.omissions, ...after.omissions, ...finalized.omissions]) { + if (omitted.some( + (current) => current.path === omission.path && current.reason === omission.reason + )) { + continue; + } + omit(omission); + } + } else { + await options.gitSelectionHooks?.afterInitialSelection?.(); + for (const relative of selectionPaths(gitSelection)) { + if (!isSnapshotProjectRef(paths, relative)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + if (!snapshotPolicyIncludes(policy, relative, execution)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + const target = path10.resolve(projectRoot, ...relative.split("/")); + if (target === configFile || target === selectionFile || denylist.some((denied) => sameOrInside(denied, target))) { + continue; + } + if (gitSelection.gitlinks.has(relative)) { + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size: 0, type: "file", reason: "file-count" }); + continue; + } + let realGitlink; + let gitlinkStat; + try { + [realGitlink, gitlinkStat] = await Promise.all([fs10.realpath(target), fs10.lstat(target)]); + } catch { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (gitlinkStat.isSymbolicLink() || !gitlinkStat.isDirectory() || !isInsidePath(physicalProjectRoot, realGitlink) || sameOrInside(physicalNativeRoot, realGitlink)) { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + try { + const workingTree = await inspectGitlinkWorkingTree(execution, realGitlink); + if (workingTree.dirty) { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-dirty" + }); + continue; + } + await recordCapturedEntry( + { + path: relative, + hash: workingTree.hash, + size: 0, + type: "file" + }, + { + kind: "gitlink", + target, + realTarget: realGitlink, + stat: gitlinkStat, + hash: workingTree.hash + } + ); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + } + continue; + } + let before; + try { + before = await fs10.lstat(target); + } catch (error) { + if (isChangedDuringReadError(error) && gitSelection.tracked.has(relative)) { + capturedTrackedAbsences.set(relative, target); + continue; + } + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: null, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + continue; + } + if (before.isSymbolicLink()) { + await captureSymbolicLink(target, relative, before); + continue; + } + if (!before.isFile()) { + if (!gitSelection.nestedRepositories.has(relative)) { + omit({ path: relative, size: null, type: "other", reason: "changed-during-read" }); + } + continue; + } + await captureFile(target, relative, before); + } + await revalidateCapturedEntries(); + await finalizeNativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + gitSelection, + options.gitSelectionHooks?.outputChunkBytes + ); + for (const omission of gitSelection.omissions) omit(omission); + if (gitSelection.overflow) foldGitSelectionOverflow(gitSelection.overflow); + } + entries.sort((left, right) => left.path.localeCompare(right.path, "en")); + omitted.sort((left, right) => left.path.localeCompare(right.path, "en")); + const capture = gitSelection === null ? { + provider: "physical-tree", + ...physicalSelectionEvidence ? { physicalSelection: physicalSelectionEvidence } : {} + } : { + provider: "git", + ...gitSelection.evidence ? { gitSelection: gitSelection.evidence } : {} + }; + const buildManifest = () => ({ + schema: "comet.native.content-snapshot.v1", + origin: options.origin ?? "explicit", + capture, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + complete: omittedCount === 0, + limits, + ...policy ? { policy: policy.manifest } : {}, + entries, + omitted, + omittedCount, + ...overflowCount > 0 ? { + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${overflowHash}`, + hash: overflowHash, + count: overflowCount + } + } : {} + }); + let manifest = buildManifest(); + while (serializedManifestBytes(manifest) > limits.maxManifestBytes) { + const compactableOmissionCount = omitted.filter( + (omission) => !isSelectionIntegrityOmission(omission) + ).length; + if (compactableOmissionCount > 0) { + const removeCount = Math.max(1, Math.ceil(omitted.length / 4)); + for (let removed = 0; removed < removeCount; removed += 1) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) break; + foldOverflow(omission); + } + } else if (entries.length > 0) { + const removeCount = Math.max(1, Math.ceil(entries.length / 4)); + for (const entry2 of entries.splice(-removeCount)) { + omittedCount += 1; + foldManifestEntryOverflow(entry2); + } + } else { + throw new Error("Native snapshot manifest byte limit is too small for its metadata"); + } + manifest = buildManifest(); + } + return manifest; +} +async function createNativeCurrentContentSnapshot(paths, baseline, options = {}) { + const config = await readProjectConfig(paths.projectRoot); + const settings = config?.native.snapshot ?? DEFAULT_NATIVE_SNAPSHOT_CONFIG; + return createNativeContentSnapshot(paths, { + ...options, + policy: baseline.policy, + limits: { + maxFiles: settings.max_files, + maxFileBytes: settings.max_total_bytes, + maxTotalBytes: settings.max_total_bytes, + maxDurationMs: settings.max_duration_ms + }, + deadlineMs: settings.max_duration_ms + }); +} +async function writeNativeBaselineManifest(paths, name, manifest) { + const file = nativeBaselineManifestFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + await atomicWriteJson(file, parseNativeContentSnapshotManifest(manifest)); +} +async function readNativeBaselineManifest(paths, name) { + const file = nativeBaselineManifestFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const source = await readNativeProtectedTextFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES, + label: "Native baseline snapshot manifest" + }); + return parseNativeContentSnapshotManifest(JSON.parse(source.text)); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} + +// domains/comet-native/native-trajectory-recovery.ts +import { createHash as createHash7 } from "crypto"; +import path14 from "path"; + +// domains/engine/storage-layout.ts +import path11 from "path"; +var NATIVE_RUN_STORAGE = /* @__PURE__ */ Object.freeze({ + stateRef: "runtime/run-state.json", + pendingRef: "runtime/pending-action.json", + trajectoryRef: "runtime/trajectory.jsonl", + contextRef: "runtime/context.md", + artifactsRef: "runtime/artifacts.json", + checkpointRef: "runtime/checkpoints/latest.json", + snapshotsRef: "runtime/skill-snapshots" +}); +function assertRunStorageRef(value) { + if (value.length === 0 || path11.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error("Run storage ref must stay inside the Run root"); + } +} +function assertRunStorageLayout(storage) { + for (const value of Object.values(storage)) assertRunStorageRef(value); +} + +// domains/comet-native/native-run-store.ts +import { createHash as createHash6 } from "node:crypto"; +import { constants as fsConstants3, promises as fs11 } from "node:fs"; +import path13 from "node:path"; +import { TextDecoder as TextDecoder4 } from "node:util"; + +// domains/engine/state.ts +import path12 from "path"; +var field = (doc, key) => { + const value = doc[key]; + return value === null || value === void 0 ? null : String(value); +}; +function requiredString(doc, key) { + const value = doc[key]; + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Invalid Run state: ${key} must be a non-empty string`); + } + return value; +} +function requiredRunReference(doc, key) { + const value = requiredString(doc, key); + if (path12.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Run state: ${key} must stay inside the change directory`); + } + return value; +} +function retries(doc) { + const raw = doc.run_retries ?? "{}"; + let value; + try { + value = typeof raw === "string" ? JSON.parse(raw) : raw; + } catch (error) { + throw new Error("Invalid Run state: run_retries must be a JSON object", { cause: error }); + } + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: run_retries must be a JSON object"); + } + for (const count of Object.values(value)) { + if (!Number.isInteger(count) || Number(count) < 0) { + throw new Error("Invalid Run state: retry counts must be non-negative integers"); + } + } + return value; +} +function runStateFromDocument(doc) { + if (!doc.run_id) return null; + const runId = requiredString(doc, "run_id"); + const skill = requiredString(doc, "skill"); + const skillVersion = requiredString(doc, "skill_version"); + const skillHash = requiredString(doc, "skill_hash"); + const pendingRef = requiredRunReference(doc, "pending_ref"); + const trajectoryRef = requiredRunReference(doc, "trajectory_ref"); + const contextRef = requiredRunReference(doc, "context_ref"); + const artifactsRef = requiredRunReference(doc, "artifacts_ref"); + const checkpointRef = requiredRunReference(doc, "checkpoint_ref"); + const iteration = Number(doc.iteration); + if (!Number.isInteger(iteration) || iteration < 0) { + throw new Error("Invalid Run state: iteration must be a non-negative integer"); + } + if (doc.orchestration !== "deterministic" && doc.orchestration !== "adaptive") { + throw new Error("Invalid Run state: orchestration must be deterministic or adaptive"); + } + if (doc.run_status !== "running" && doc.run_status !== "waiting" && doc.run_status !== "completed" && doc.run_status !== "failed") { + throw new Error("Invalid Run state: run_status is invalid"); + } + return { + runId, + skill, + skillVersion, + skillHash, + orchestration: doc.orchestration, + currentStep: field(doc, "current_step"), + iteration, + pending: field(doc, "pending"), + pendingRef, + trajectoryRef, + contextRef, + artifactsRef, + checkpointRef, + status: doc.run_status, + retries: retries(doc) + }; +} + +// domains/engine/storage-run.ts +var STORED_RUN_STATE_KEYS = /* @__PURE__ */ new Set([ + "runId", + "skill", + "skillVersion", + "skillHash", + "orchestration", + "currentStep", + "iteration", + "pending", + "pendingRef", + "trajectoryRef", + "contextRef", + "artifactsRef", + "checkpointRef", + "status", + "retries" +]); +function fromStoredState(json) { + const document = { + run_id: json.runId, + skill: json.skill, + skill_version: json.skillVersion, + skill_hash: json.skillHash, + orchestration: json.orchestration, + current_step: json.currentStep, + iteration: json.iteration, + pending: json.pending, + pending_ref: json.pendingRef, + trajectory_ref: json.trajectoryRef, + context_ref: json.contextRef, + artifacts_ref: json.artifactsRef, + checkpoint_ref: json.checkpointRef, + run_status: json.status, + run_retries: JSON.stringify(json.retries) + }; + const parsed = runStateFromDocument(document); + if (!parsed) throw new Error("Invalid Run state: runId must be a non-empty string"); + return parsed; +} +function parseStoredRunStateValue(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: stored value must be an object"); + } + const json = value; + const unknown = Object.keys(json).find( + (key) => !STORED_RUN_STATE_KEYS.has(key) + ); + if (unknown) throw new Error(`Invalid Run state: unknown field ${unknown}`); + if (json.currentStep !== null && (typeof json.currentStep !== "string" || json.currentStep.length === 0)) { + throw new Error("Invalid Run state: currentStep must be a non-empty string or null"); + } + if (json.pending !== null && (typeof json.pending !== "string" || json.pending.length === 0)) { + throw new Error("Invalid Run state: pending must be a non-empty string or null"); + } + if (!json.retries || typeof json.retries !== "object" || Array.isArray(json.retries)) { + throw new Error("Invalid Run state: retries must be an object"); + } + return fromStoredState(json); +} +function startRunWithStorage(pkg, runId, skillHash, storage) { + assertRunStorageLayout(storage); + return { + runId, + skill: pkg.definition.metadata.name, + skillVersion: pkg.definition.metadata.version, + skillHash, + orchestration: pkg.definition.orchestration.mode, + currentStep: pkg.definition.orchestration.entry ?? null, + iteration: 0, + pending: null, + pendingRef: storage.pendingRef, + trajectoryRef: storage.trajectoryRef, + contextRef: storage.contextRef, + artifactsRef: storage.artifactsRef, + checkpointRef: storage.checkpointRef, + status: "running", + retries: {} + }; +} + +// domains/comet-native/native-run-store.ts +var NATIVE_RUN_IO_LIMITS = { + runStateBytes: 256 * 1024, + trajectoryBytes: 8 * 1024 * 1024, + trajectoryEvents: 4096, + trajectoryEventBytes: 256 * 1024, + checkpointBytes: 256 * 1024, + pendingActionBytes: 256 * 1024, + contextBytes: 1024 * 1024, + artifactsBytes: 1024 * 1024 +}; +function isInside4(parent, target) { + const relative = path13.relative(parent, target); + return relative === "" || !path13.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path13.sep}`); +} +function asIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity5(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +function sameDirectoryIdentity4(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function runFile(changeDir, kind, relativePath) { + const expected = NATIVE_RUN_STORAGE[kind]; + if (relativePath !== void 0 && relativePath !== expected) { + throw new Error(`Native Run ${kind} ref must be ${expected}`); + } + const root = path13.resolve(changeDir); + const target = path13.resolve(root, ...expected.split("/")); + if (!isInside4(root, target)) throw new Error("Native Run path must stay inside its change"); + return target; +} +async function directoryIdentity2(directory) { + const stat = await fs11.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native Run parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs11.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain3(root, directory) { + const lexicalRoot = path13.resolve(root); + const lexicalDirectory = path13.resolve(directory); + if (!isInside4(lexicalRoot, lexicalDirectory)) { + throw new Error("Native Run path is outside its change"); + } + const chain = [await directoryIdentity2(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path13.relative(lexicalRoot, lexicalDirectory).split(path13.sep).filter(Boolean)) { + cursor = path13.join(cursor, segment); + let identity; + try { + identity = await directoryIdentity2(cursor); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (!isInside4(chain[0].realPath, identity.realPath)) { + throw new Error(`Native Run parent resolves outside its change: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain4(chain) { + for (const identity of chain) { + const stat = await fs11.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity4(identity, stat) || await fs11.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native Run parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded2(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readProtectedText(changeDir, file, maxBytes, label, hooks) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) return null; + await hooks?.afterParentChainCaptured?.(); + let before; + try { + before = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asIdentity(before); + const beforeRealPath = await fs11.realpath(file); + if (!isInside4(chain[0].realPath, beforeRealPath)) { + throw new Error(`${label} resolves outside its change`); + } + const flags = process.platform === "win32" ? fsConstants3.O_RDONLY : fsConstants3.O_RDONLY | fsConstants3.O_NOFOLLOW | fsConstants3.O_NONBLOCK; + let handle; + try { + handle = await fs11.open(file, flags); + } catch (error) { + throw new Error(`${label} changed while opening`, { cause: error }); + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs11.lstat(file), + fs11.realpath(file) + ]); + await verifyDirectoryChain4(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity5(beforeIdentity, opened) || !sameFileIdentity5(beforeIdentity, pathAfterOpen)) { + throw new Error(`${label} changed while opening`); + } + await hooks?.afterOpen?.(); + const bytes = await readHandleBounded2(handle, maxBytes, label); + await hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs11.lstat(file), + fs11.realpath(file) + ]); + await verifyDirectoryChain4(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity5(beforeIdentity, afterHandle) || !sameFileIdentity5(beforeIdentity, afterPath)) { + throw new Error(`${label} changed while reading`); + } + let text; + try { + text = new TextDecoder4("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`${label} is not valid UTF-8`, { cause: error }); + } + return { + text, + target: { exists: true, identity: beforeIdentity, realPath: beforeRealPath } + }; + } finally { + await handle.close(); + } +} +async function captureTarget(changeDir, file, label) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) return { exists: false }; + let stat; + try { + stat = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return { exists: false }; + throw error; + } + if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`${label} must be a regular file`); + const realPath = await fs11.realpath(file); + if (!isInside4(chain[0].realPath, realPath)) + throw new Error(`${label} resolves outside its change`); + await verifyDirectoryChain4(chain); + return { exists: true, identity: asIdentity(stat), realPath }; +} +async function verifyTarget(changeDir, file, expected, label) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) { + if (!expected.exists) return; + throw new Error(`${label} parent changed before commit`); + } + let stat; + try { + stat = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT" && !expected.exists) return; + throw new Error(`${label} changed before commit`, { cause: error }); + } + if (!expected.exists || !stat.isFile() || stat.isSymbolicLink() || !sameFileIdentity5(expected.identity, stat) || await fs11.realpath(file) !== expected.realPath) { + throw new Error(`${label} changed before commit`); + } + await verifyDirectoryChain4(chain); +} +async function writeProtectedText(options) { + const bytes = Buffer.byteLength(options.content, "utf8"); + if (bytes > options.maxBytes) { + throw new Error(`${options.label} exceeds ${options.maxBytes} bytes`); + } + const expected = options.expected ?? await captureTarget(options.changeDir, options.file, options.label); + if (expected.exists && expected.identity.size > options.maxBytes) { + throw new Error(`${options.label} exceeds ${options.maxBytes} bytes`); + } + await atomicWriteText(options.file, options.content, { + containedRoot: options.changeDir, + beforeCommit: async () => { + await options.hooks?.beforeCommit?.(); + await verifyTarget(options.changeDir, options.file, expected, options.label); + } + }); + const persisted = await readProtectedText( + options.changeDir, + options.file, + options.maxBytes, + options.label + ); + if (!persisted || persisted.text !== options.content) { + throw new Error(`${options.label} commit could not be verified`); + } +} +function parseJson(text, label) { + try { + return JSON.parse(text); + } catch (error) { + throw new Error(`${label} contains invalid JSON`, { cause: error }); + } +} +function jsonText(value) { + return `${JSON.stringify(value, null, 2)} +`; +} +function parseNativeStoredRunStateValue(value) { + return parseStoredRunStateValue(value); +} +function startNativeRun(pkg, runId, skillHash) { + return startRunWithStorage(pkg, runId, skillHash, NATIVE_RUN_STORAGE); +} +async function readNativeRunState(changeDir, hooks) { + const file = runFile(changeDir, "stateRef"); + const raw = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.runStateBytes, + "Native Run state", + hooks + ); + return raw ? parseStoredRunStateValue(parseJson(raw.text, "Native Run state")) : null; +} +async function writeNativeRunState(changeDir, state, hooks) { + const validated = parseStoredRunStateValue(state); + await writeProtectedText({ + changeDir, + file: runFile(changeDir, "stateRef"), + content: jsonText(validated), + maxBytes: NATIVE_RUN_IO_LIMITS.runStateBytes, + label: "Native Run state", + hooks + }); +} +async function readNativeTrajectory(changeDir, relativePath, hooks) { + const file = runFile(changeDir, "trajectoryRef", relativePath); + const raw = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory", + hooks + ); + if (!raw) return []; + const lines = raw.text.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter(({ line }) => line.length > 0); + if (lines.length > NATIVE_RUN_IO_LIMITS.trajectoryEvents) { + throw new Error( + `Native Run trajectory exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEvents} events` + ); + } + return lines.map(({ line, number }) => { + try { + return JSON.parse(line); + } catch (error) { + throw new Error(`Invalid Native Run trajectory event at line ${number}`, { cause: error }); + } + }); +} +async function readNativeTrajectoryText(changeDir, relativePath, hooks) { + const raw = await readProtectedText( + changeDir, + runFile(changeDir, "trajectoryRef", relativePath), + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory", + hooks + ); + return raw?.text ?? null; +} +async function replaceNativeTrajectoryText(changeDir, relativePath, content, expectedHash, hooks) { + if (!/^[a-f0-9]{64}$/u.test(expectedHash)) { + throw new Error("Native Run trajectory expected hash is invalid"); + } + const file = runFile(changeDir, "trajectoryRef", relativePath); + const existing = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory" + ); + if (!existing) throw new Error("Native Run trajectory disappeared before repair"); + const actualHash = createHash6("sha256").update(existing.text, "utf8").digest("hex"); + if (actualHash !== expectedHash) { + throw new Error("Native Run trajectory changed before repair"); + } + await writeProtectedText({ + changeDir, + file, + content, + maxBytes: NATIVE_RUN_IO_LIMITS.trajectoryBytes, + label: "Native Run trajectory", + expected: existing.target, + hooks + }); +} +async function appendNativeTrajectory(changeDir, relativePath, event, hooks) { + const file = runFile(changeDir, "trajectoryRef", relativePath); + const line = `${JSON.stringify(event)} +`; + if (Buffer.byteLength(line, "utf8") > NATIVE_RUN_IO_LIMITS.trajectoryEventBytes) { + throw new Error( + `Native Run trajectory event exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEventBytes} bytes` + ); + } + const existing = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory" + ); + const existingEvents = existing ? existing.text.split(/\r?\n/u).filter((value) => value.length > 0).length : 0; + if (existingEvents >= NATIVE_RUN_IO_LIMITS.trajectoryEvents) { + throw new Error( + `Native Run trajectory exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEvents} events` + ); + } + await writeProtectedText({ + changeDir, + file, + content: `${existing?.text ?? ""}${line}`, + maxBytes: NATIVE_RUN_IO_LIMITS.trajectoryBytes, + label: "Native Run trajectory", + expected: existing?.target ?? { exists: false }, + hooks + }); +} +async function readNativeCheckpoint(changeDir, relativePath, hooks) { + const raw = await readProtectedText( + changeDir, + runFile(changeDir, "checkpointRef", relativePath), + NATIVE_RUN_IO_LIMITS.checkpointBytes, + "Native Run checkpoint", + hooks + ); + return raw ? parseJson(raw.text, "Native Run checkpoint") : null; +} +async function writeNativeCheckpoint(changeDir, relativePath, checkpoint, hooks) { + await writeProtectedText({ + changeDir, + file: runFile(changeDir, "checkpointRef", relativePath), + content: jsonText(checkpoint), + maxBytes: NATIVE_RUN_IO_LIMITS.checkpointBytes, + label: "Native Run checkpoint", + hooks + }); +} + +// domains/comet-native/native-trajectory-recovery.ts +var CHANGE_NAME_PATTERN2 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var NativeTrajectoryRepairRequiredError = class extends Error { + constructor(inspection) { + super( + inspection.status === "repairable" ? `Native trajectory has an incomplete final line at ${inspection.file}:${inspection.line}; run doctor --repair` : `Native trajectory is invalid at ${inspection.file}:${inspection.line}: ${inspection.message}` + ); + this.inspection = inspection; + this.name = "NativeTrajectoryRepairRequiredError"; + } + inspection; + code = "native-trajectory-tail-repair-required"; +}; +function sha256Buffer(value) { + return createHash7("sha256").update(value).digest("hex"); +} +function trajectoryFile(paths, name) { + if (!CHANGE_NAME_PATTERN2.test(name)) throw new Error(`Invalid Native change name: ${name}`); + const changeDir = path14.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, changeDir)) throw new Error("Native change path escaped"); + return path14.join(changeDir, "runtime", "trajectory.jsonl"); +} +function parseCompleteLines(content) { + const lines = content.split(/\n/u); + let count = 0; + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index].endsWith("\r") ? lines[index].slice(0, -1) : lines[index]; + if (line.length === 0) continue; + count = index + 1; + try { + JSON.parse(line); + } catch (error) { + return { status: "invalid", line: index + 1, message: error.message }; + } + } + return { status: "valid", count }; +} +function looksTruncated(error, content) { + if (/Unexpected end|Unterminated string/iu.test(error.message)) return true; + const position = /position (\d+)/iu.exec(error.message)?.[1]; + return position !== void 0 && Number(position) >= Math.max(0, content.length - 1); +} +function analyzeTrajectory(file, source) { + const lastNewline = source.lastIndexOf(10); + const prefix = source.subarray(0, lastNewline + 1); + const complete = parseCompleteLines(prefix.toString("utf8")); + if (complete.status === "invalid") { + return { status: "invalid", file, line: complete.line, message: complete.message }; + } + if (lastNewline === source.length - 1) return { status: "clean", file }; + const tail = source.subarray(lastNewline + 1); + const tailText = tail.toString("utf8"); + const line = complete.count + 1; + let reason; + let target; + try { + JSON.parse(tailText.endsWith("\r") ? tailText.slice(0, -1) : tailText); + reason = "missing-newline"; + target = Buffer.concat([source, Buffer.from("\n")]); + } catch (error) { + if (!looksTruncated(error, tailText)) { + return { status: "invalid", file, line, message: error.message }; + } + reason = "incomplete-json"; + target = prefix; + } + const inspection = { + status: "repairable", + file, + reason, + line, + originalHash: sha256Buffer(source), + targetHash: sha256Buffer(target), + tailHash: sha256Buffer(tail), + discardedBytes: reason === "incomplete-json" ? tail.length : 0 + }; + return { inspection, targetContent: target.toString("utf8") }; +} +async function inspectFile(paths, name) { + const file = trajectoryFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + const changeDir = path14.join(paths.changesDir, name); + const content = await readNativeTrajectoryText(changeDir, NATIVE_RUN_STORAGE.trajectoryRef); + return content === null ? { status: "clean", file } : analyzeTrajectory(file, Buffer.from(content)); +} +async function inspectNativeTrajectoryTail(paths, name) { + const result2 = await inspectFile(paths, name); + return "inspection" in result2 ? result2.inspection : result2; +} +async function assertNativeTrajectoryHealthy(paths, name) { + const inspection = await inspectNativeTrajectoryTail(paths, name); + if (inspection.status !== "clean") throw new NativeTrajectoryRepairRequiredError(inspection); +} +async function repairNativeTrajectoryTail(paths, name, hooks) { + return withNativeMutationLock(paths, `repair trajectory tail for ${name}`, async () => { + const result2 = await inspectFile(paths, name); + if (!("inspection" in result2)) { + if (result2.status === "clean") return null; + throw new NativeTrajectoryRepairRequiredError(result2); + } + try { + await replaceNativeTrajectoryText( + path14.join(paths.changesDir, name), + NATIVE_RUN_STORAGE.trajectoryRef, + result2.targetContent, + result2.inspection.originalHash, + { beforeCommit: hooks?.beforeCommit } + ); + } catch (error) { + if (/changed (?:before|while)/iu.test(error.message)) { + throw new Error( + `Native trajectory changed while preparing tail repair for ${name}; inspect it again before retrying`, + { cause: error } + ); + } + throw error; + } + const repaired = await inspectNativeTrajectoryTail(paths, name); + if (repaired.status !== "clean") { + throw new Error(`Native trajectory tail repair did not produce a clean file for ${name}`); + } + return result2.inspection; + }); +} + +// domains/comet-native/native-workspace.ts +import { createHash as createHash8 } from "node:crypto"; +import { promises as fs12 } from "node:fs"; +import path15 from "node:path"; +var HASH_PATTERN2 = /^[a-f0-9]{64}$/u; +var MAX_WORKSPACE_IDENTITY_BYTES = 16 * 1024; +var NATIVE_WORKSPACE_ADVISORY_CODES = /* @__PURE__ */ new Set([ + "workspace-root-changed", + "workspace-inspection-unavailable" +]); +function isNativeWorkspaceAdvisoryCode(code) { + return NATIVE_WORKSPACE_ADVISORY_CODES.has(code); +} +function portableRelative2(parent, target) { + const relative = path15.relative(parent, target); + if (path15.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path15.sep}`)) { + return null; + } + return relative.replaceAll("\\", "/") || "."; +} +function hasControlCharacter(value) { + return Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }); +} +function normalizedPortableRef(value, label) { + if (value.length === 0 || hasControlCharacter(value) || value.includes("\\") || path15.posix.isAbsolute(value) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..")) { + throw new Error(`${label} must be a portable project-relative path`); + } + const normalized = path15.posix.normalize(value); + if (normalized !== value || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return normalized; +} +function identityHash(tag, value) { + return createHash8("sha256").update(`${tag} +${value}`).digest("hex"); +} +async function physicalDirectoryIdentity(tag, value) { + const realPath = await fs12.realpath(value); + const stat = await fs12.lstat(realPath); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error("Native workspace identity requires a real directory"); + } + const normalizedPath = process.platform === "win32" ? path15.normalize(realPath).toLowerCase() : realPath; + return identityHash(tag, `${normalizedPath} +${stat.dev} +${stat.ino} +${stat.birthtimeMs}`); +} +async function directoryPathIdentity(tag, value) { + const realPath = await fs12.realpath(value); + const stat = await fs12.lstat(realPath); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error("Native workspace identity requires a real directory"); + } + const normalizedPath = process.platform === "win32" ? path15.normalize(realPath).toLowerCase() : realPath; + return identityHash(tag, normalizedPath); +} +function isoTimestamp(value) { + if (typeof value !== "string") throw new Error("Native workspace capturedAt is invalid"); + const parsed = new Date(value); + if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) { + throw new Error("Native workspace capturedAt is invalid"); + } + return value; +} +function assertIdentity(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native workspace identity must be an object"); + } + const root = value; + const allowed = /* @__PURE__ */ new Set([ + "schema", + "capturedAt", + "capturedRevision", + "nativeRootRef", + "projectRootId", + "nativeRootId", + "projectRootPathId", + "nativeRootPathId", + "sessionHash" + ]); + const unknown = Object.keys(root).filter((key) => !allowed.has(key)); + if (unknown.length > 0) { + throw new Error(`Native workspace identity has unknown field(s): ${unknown.join(", ")}`); + } + if (root.schema !== "comet.native.workspace.v2") { + throw new Error("Unsupported Native workspace identity"); + } + if (!Number.isSafeInteger(root.capturedRevision) || root.capturedRevision < 1 || typeof root.nativeRootRef !== "string" || !HASH_PATTERN2.test(String(root.projectRootId)) || !HASH_PATTERN2.test(String(root.nativeRootId))) { + throw new Error("Native workspace identity is invalid"); + } + isoTimestamp(root.capturedAt); + normalizedPortableRef(root.nativeRootRef, "Native workspace root ref"); + const hasProjectPathId = root.projectRootPathId !== void 0; + const hasNativePathId = root.nativeRootPathId !== void 0; + if (hasProjectPathId !== hasNativePathId) { + throw new Error("Native workspace path identities must be provided together"); + } + if (hasProjectPathId && !HASH_PATTERN2.test(String(root.projectRootPathId)) || hasNativePathId && !HASH_PATTERN2.test(String(root.nativeRootPathId))) { + throw new Error("Native workspace path identity is invalid"); + } + if (root.sessionHash !== void 0 && !HASH_PATTERN2.test(String(root.sessionHash))) { + throw new Error("Native workspace session hash is invalid"); + } +} +function nativeWorkspaceFile(paths, name) { + return path15.join(paths.changesDir, name, "runtime", "workspace.json"); +} +function nativeWorkspaceRef(paths, name) { + const relative = portableRelative2(paths.nativeRoot, nativeWorkspaceFile(paths, name)); + if (!relative || relative === ".") throw new Error("Native workspace file escaped its root"); + return normalizedPortableRef(relative, "Native workspace file ref"); +} +async function readNativeWorkspaceValue(paths, name) { + try { + const artifact = await readNativeBoundedTextFile({ + root: paths.nativeRoot, + ref: nativeWorkspaceRef(paths, name), + maxBytes: MAX_WORKSPACE_IDENTITY_BYTES + }); + return JSON.parse(artifact.text); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function nativeWorkspaceIdentityNeedsMigration(paths, name) { + const value = await readNativeWorkspaceValue(paths, name); + if (value === null) return false; + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native workspace identity must be an object"); + } + if (value.schema === "comet.native.workspace.v1") return true; + assertIdentity(value); + return value.projectRootPathId === void 0 || value.nativeRootPathId === void 0; +} +async function inspectNativeWorkspaceIdentity(options) { + if (!Number.isSafeInteger(options.revision) || options.revision < 1) { + throw new Error("Native workspace revision must be a positive integer"); + } + const nativeRootRef3 = portableRelative2(options.paths.projectRoot, options.paths.nativeRoot); + if (!nativeRootRef3) throw new Error("Native root is outside the project root"); + const [projectRootId, nativeRootId, projectRootPathId, nativeRootPathId] = await Promise.all([ + physicalDirectoryIdentity("comet.native.workspace-project-root.v2", options.paths.projectRoot), + physicalDirectoryIdentity("comet.native.workspace-native-root.v2", options.paths.nativeRoot), + directoryPathIdentity("comet.native.workspace-project-root-path.v2", options.paths.projectRoot), + directoryPathIdentity("comet.native.workspace-native-root-path.v2", options.paths.nativeRoot) + ]); + const capturedAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString(); + const identity = { + schema: "comet.native.workspace.v2", + capturedAt, + capturedRevision: options.revision, + nativeRootRef: nativeRootRef3, + projectRootId, + nativeRootId, + projectRootPathId, + nativeRootPathId, + ...options.sessionId ? { + sessionHash: identityHash( + "comet.native.workspace-session.v2", + `${projectRootId} +${nativeRootId} +${options.sessionId}` + ) + } : {} + }; + assertIdentity(identity); + return identity; +} +async function writeNativeWorkspaceIdentity(options) { + const identity = await inspectNativeWorkspaceIdentity(options); + const file = nativeWorkspaceFile(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, file); + await atomicWriteJson(file, identity, { containedRoot: options.paths.nativeRoot }); + return identity; +} +async function readNativeWorkspaceIdentity(paths, name) { + const value = await readNativeWorkspaceValue(paths, name); + if (value === null) return null; + if (value && typeof value === "object" && !Array.isArray(value) && value.schema === "comet.native.workspace.v1") { + return null; + } + assertIdentity(value); + return value; +} +async function migrateLegacyNativeWorkspaceIdentity(options) { + if (!await nativeWorkspaceIdentityNeedsMigration(options.paths, options.name)) { + return null; + } + return writeNativeWorkspaceIdentity(options); +} +async function inspectNativeWorkspaceAdvisory(options) { + assertIdentity(options.identity); + const current = await inspectNativeWorkspaceIdentity({ + paths: options.paths, + name: "workspace-advisory", + revision: options.identity.capturedRevision + }); + const driftComponents = []; + const codes = []; + if (current.nativeRootRef !== options.identity.nativeRootRef) { + driftComponents.push("native-root-ref"); + } + if (options.identity.projectRootPathId && options.identity.nativeRootPathId) { + if (current.projectRootPathId !== options.identity.projectRootPathId) { + driftComponents.push("project-root-path"); + } + if (current.nativeRootPathId !== options.identity.nativeRootPathId) { + driftComponents.push("native-root-path"); + } + } else { + if (current.projectRootId !== options.identity.projectRootId) { + driftComponents.push("project-root-legacy-identity"); + } + if (current.nativeRootId !== options.identity.nativeRootId) { + driftComponents.push("native-root-legacy-identity"); + } + } + const onlyUnstableWindowsLegacyHashes = process.platform === "win32" && driftComponents.length > 0 && driftComponents.every( + (component) => component === "project-root-legacy-identity" || component === "native-root-legacy-identity" + ); + if (onlyUnstableWindowsLegacyHashes) { + codes.push("workspace-inspection-unavailable"); + } else if (driftComponents.length > 0) { + codes.push("workspace-root-changed"); + } + return { + state: codes.length === 0 ? "aligned" : codes.includes("workspace-root-changed") ? "drifted" : "unknown", + findingCodes: codes, + driftComponents + }; +} + +// domains/comet-native/native-types.ts +var NATIVE_RUNTIME_PROTOCOL_VERSION = 3; +var NATIVE_CHANGE_SCHEMA = "comet.native.v3"; +var NATIVE_V2_CHANGE_SCHEMA = "comet.native.v2"; +var NATIVE_LEGACY_CHANGE_SCHEMA = "comet.native.v1"; +var NATIVE_TRANSITION_SCHEMA = "comet.native.transition.v3"; +var NATIVE_V2_TRANSITION_SCHEMA = "comet.native.transition.v2"; +var NATIVE_LEGACY_TRANSITION_SCHEMA = "comet.native.transition.v1"; + +// domains/comet-native/native-change.ts +var CHANGE_KEYS = [ + "schema", + "name", + "language", + "phase", + "brief", + "approval", + "spec_changes", + "verification_result", + "verification_report", + "archived", + "created_at", + "run_id" +]; +var LEGACY_CHANGE_KEYS = new Set(CHANGE_KEYS); +var V2_CHANGE_KEYS = /* @__PURE__ */ new Set([...CHANGE_KEYS, "minimum_runtime_version", "revision"]); +var CURRENT_CHANGE_KEYS = /* @__PURE__ */ new Set([ + ...V2_CHANGE_KEYS, + "approved_contract_hash", + "implementation_scope", + "verification_evidence", + "partial_allowance" +]); +var SPEC_CHANGE_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "source", "base_hash"]); +var PHASES = /* @__PURE__ */ new Set(["shape", "build", "verify", "archive"]); +var APPROVALS = /* @__PURE__ */ new Set(["implicit", "confirmed"]); +var VERIFY_RESULTS = /* @__PURE__ */ new Set(["pending", "pass", "fail"]); +var NATIVE_CHANGE_STATE_FILE = "comet-state.yaml"; +var HASH_PATTERN3 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CONTENT_ADDRESSED_REF_PATTERN = /^runtime\/evidence\/(scopes|allowances|verifications)\/([a-f0-9]{64})\.json$/u; +var NativeSchemaMigrationRequiredError = class extends Error { + constructor(change, schema) { + super( + `Native change ${change} uses ${schema}; run comet native doctor ${change} --repair before mutating it` + ); + this.change = change; + this.schema = schema; + this.name = "NativeSchemaMigrationRequiredError"; + } + change; + schema; + code = "native-schema-migration-required"; +}; +var NativeRuntimeCompatibilityError = class extends Error { + constructor(schema, minimumRuntimeVersion) { + super( + schema !== NATIVE_CHANGE_SCHEMA || minimumRuntimeVersion === null ? `Unsupported Native change schema ${schema} for runtime protocol ${NATIVE_RUNTIME_PROTOCOL_VERSION}` : `Native change ${schema} requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + this.schema = schema; + this.minimumRuntimeVersion = minimumRuntimeVersion; + this.name = "NativeRuntimeCompatibilityError"; + } + schema; + minimumRuntimeVersion; + code = "native-runtime-incompatible"; +}; +var NativeChangeRevisionConflictError = class extends Error { + constructor(change, expectedRevision, actualRevision) { + super( + `Native change ${change} revision conflict: expected ${expectedRevision}, actual ${actualRevision}` + ); + this.change = change; + this.expectedRevision = expectedRevision; + this.actualRevision = actualRevision; + this.name = "NativeChangeRevisionConflictError"; + } + change; + expectedRevision; + actualRevision; + code = "native-change-revision-conflict"; +}; +var NativeBaselineIncompleteError = class extends Error { + constructor(change, omittedCount, omittedByReason, samplePaths, sampleTruncated, effectiveLimits = null, policyHash = null) { + super( + `Native change ${change} baseline is incomplete (${omittedCount} omitted entr${omittedCount === 1 ? "y" : "ies"}). Adjust native.snapshot scope or resource budgets in .comet/config.yaml, then retry.` + ); + this.change = change; + this.omittedCount = omittedCount; + this.omittedByReason = omittedByReason; + this.samplePaths = samplePaths; + this.sampleTruncated = sampleTruncated; + this.effectiveLimits = effectiveLimits; + this.policyHash = policyHash; + this.name = "NativeBaselineIncompleteError"; + } + change; + omittedCount; + omittedByReason; + samplePaths; + sampleTruncated; + effectiveLimits; + policyHash; + code = "native-baseline-incomplete"; +}; +var NATIVE_BRIEF_TEMPLATE = [ + "# Outcome", + "", + "# Scope", + "", + "# Non-goals", + "", + "# Acceptance examples", + "", + "# Constraints and invariants", + "", + "# Decisions", + "", + "# Open questions", + "", + "# Verification expectations", + "" +].join("\n"); +function record4(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown4(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function assertNativeName(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native change name: ${value}`); +} +function assertCapabilityId(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native capability id: ${value}`); +} +function assertRelativeRef(value, label) { + if (value.length === 0 || path16.isAbsolute(value) || /^(?:[A-Za-z]:|~|[\\/])/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native change`); + } +} +function parseSpecChange(value, index) { + const item = record4(value, `spec_changes[${index}]`); + rejectUnknown4(item, SPEC_CHANGE_KEYS, `spec_changes[${index}]`); + if (typeof item.capability !== "string") throw new Error("spec change capability is required"); + assertCapabilityId(item.capability); + if (item.operation !== "create" && item.operation !== "replace" && item.operation !== "remove") { + throw new Error(`Invalid spec operation for ${item.capability}`); + } + const source = item.source; + const baseHash = item.base_hash; + if (source !== void 0 && typeof source !== "string") { + throw new Error(`Spec source for ${item.capability} must be a string`); + } + if (typeof source === "string") assertRelativeRef(source, `Spec source for ${item.capability}`); + if (item.operation === "create") { + if (!source) throw new Error(`Create spec ${item.capability} requires source`); + if (baseHash !== null) + throw new Error(`Create spec ${item.capability} requires null base_hash`); + } else if (item.operation === "replace") { + if (!source) throw new Error(`Replace spec ${item.capability} requires source`); + if (typeof baseHash !== "string" || !HASH_PATTERN3.test(baseHash)) { + throw new Error(`Replace spec ${item.capability} requires a SHA-256 base_hash`); + } + } else { + if (source !== void 0) throw new Error(`Remove spec ${item.capability} forbids source`); + if (typeof baseHash !== "string" || !HASH_PATTERN3.test(baseHash)) { + throw new Error(`Remove spec ${item.capability} requires a SHA-256 base_hash`); + } + } + return { + capability: item.capability, + operation: item.operation, + ...typeof source === "string" ? { source } : {}, + base_hash: baseHash + }; +} +function validDate(value) { + if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) return false; + return (/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`)).toISOString().slice(0, 10) === value; +} +function parseChangeFields(root, knownKeys) { + rejectUnknown4(root, knownKeys, NATIVE_CHANGE_STATE_FILE); + if (typeof root.name !== "string") throw new Error("Native change name is required"); + assertNativeName(root.name); + if (root.language !== "en" && root.language !== "zh-CN") { + throw new Error("Native change language must be en or zh-CN"); + } + if (typeof root.phase !== "string" || !PHASES.has(root.phase)) { + throw new Error("Native change phase is invalid"); + } + if (root.brief !== "brief.md") throw new Error("Native change brief must be brief.md"); + if (root.approval !== null && !APPROVALS.has(root.approval)) { + throw new Error("Native change approval is invalid"); + } + if (!Array.isArray(root.spec_changes)) throw new Error("Native spec_changes must be an array"); + const specChanges = root.spec_changes.map(parseSpecChange); + const duplicates = specChanges.map((change) => change.capability).filter((capability, index, all) => all.indexOf(capability) !== index); + if (duplicates.length > 0) { + throw new Error( + `Duplicate Native capability operation: ${[...new Set(duplicates)].join(", ")}` + ); + } + if (typeof root.verification_result !== "string" || !VERIFY_RESULTS.has(root.verification_result)) { + throw new Error("Native verification_result is invalid"); + } + if (root.verification_report !== null && typeof root.verification_report !== "string") { + throw new Error("Native verification_report must be a string or null"); + } + if (typeof root.verification_report === "string") { + assertRelativeRef(root.verification_report, "Native verification_report"); + } + if (typeof root.archived !== "boolean") throw new Error("Native archived must be boolean"); + if (typeof root.created_at !== "string" || !validDate(root.created_at)) { + throw new Error("Native created_at must be a valid YYYY-MM-DD date"); + } + if (root.run_id !== null && (typeof root.run_id !== "string" || root.run_id.length === 0)) { + throw new Error("Native run_id must be a non-empty string or null"); + } + return { + name: root.name, + language: root.language, + phase: root.phase, + brief: "brief.md", + approval: root.approval, + spec_changes: specChanges, + verification_result: root.verification_result, + verification_report: root.verification_report, + archived: root.archived, + created_at: root.created_at, + run_id: root.run_id + }; +} +function parseLegacyNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_LEGACY_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_LEGACY_CHANGE_SCHEMA}`); + } + return { + schema: NATIVE_LEGACY_CHANGE_SCHEMA, + ...parseChangeFields(root, LEGACY_CHANGE_KEYS) + }; +} +function positiveInteger2(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function contentAddressedRef(value, label, kind) { + if (value === null) return null; + const match = typeof value === "string" ? CONTENT_ADDRESSED_REF_PATTERN.exec(value) : null; + if (!match || match[1] !== kind) { + throw new Error( + `${label} must be null or runtime/evidence/${kind}/.json relative to the Native change` + ); + } + return value; +} +function approvedContractHash(value) { + if (value === void 0 || value === null) return null; + if (typeof value !== "string" || !HASH_PATTERN3.test(value)) { + throw new Error("Native approved_contract_hash must be null or a SHA-256 hash"); + } + return value; +} +function parseV2NativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_V2_CHANGE_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native v2 minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error(`Native ${NATIVE_V2_CHANGE_SCHEMA} minimum_runtime_version must be 2`); + } + return { + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision: positiveInteger2(root.revision, "Native v2 revision"), + ...parseChangeFields(root, V2_CHANGE_KEYS) + }; +} +function parseNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA || root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const previous = root.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? parseLegacyNativeChangeValue(root) : parseV2NativeChangeValue(root); + throw new NativeSchemaMigrationRequiredError(previous.name, previous.schema); + } + throw new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + typeof root.minimum_runtime_version === "number" ? root.minimum_runtime_version : null + ); + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native ${root.schema} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger2(root.revision, "Native revision"); + const fields = parseChangeFields(root, CURRENT_CHANGE_KEYS); + const approvalHash = approvedContractHash(root.approved_contract_hash); + if (fields.approval === null && approvalHash !== null) { + throw new Error("Native approved_contract_hash requires an approval"); + } + return { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + ...fields, + approved_contract_hash: approvalHash, + implementation_scope: contentAddressedRef( + root.implementation_scope, + "Native implementation_scope", + "scopes" + ), + verification_evidence: contentAddressedRef( + root.verification_evidence, + "Native verification_evidence", + "verifications" + ), + partial_allowance: contentAddressedRef( + root.partial_allowance, + "Native partial_allowance", + "allowances" + ) + }; +} +function inspectNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + const state2 = parseLegacyNativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: 1, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const state2 = parseV2NativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: state2.minimum_runtime_version, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + const minimumRuntimeVersion2 = typeof root.minimum_runtime_version === "number" && Number.isSafeInteger(root.minimum_runtime_version) ? root.minimum_runtime_version : null; + return { + status: "runtime-incompatible", + schema: typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion: minimumRuntimeVersion2, + state: null, + message: new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion2 + ).message + }; + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + return { + status: "runtime-incompatible", + schema: root.schema, + minimumRuntimeVersion, + state: null, + message: new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion).message + }; + } + const state = parseNativeChangeValue(root); + return { + status: "current", + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + state + }; +} +function nativeChangeDocument(state) { + const parsed = parseNativeChangeValue(state); + return { + schema: parsed.schema, + minimum_runtime_version: parsed.minimum_runtime_version, + revision: parsed.revision, + name: parsed.name, + language: parsed.language, + phase: parsed.phase, + brief: parsed.brief, + approval: parsed.approval, + approved_contract_hash: parsed.approved_contract_hash ?? null, + spec_changes: parsed.spec_changes.map((change) => ({ + capability: change.capability, + operation: change.operation, + ...change.source ? { source: change.source } : {}, + base_hash: change.base_hash + })), + verification_result: parsed.verification_result, + verification_report: parsed.verification_report, + implementation_scope: parsed.implementation_scope, + verification_evidence: parsed.verification_evidence, + partial_allowance: parsed.partial_allowance, + archived: parsed.archived, + created_at: parsed.created_at, + run_id: parsed.run_id + }; +} +function nativeV2ChangeDocument(state) { + const parsed = parseV2NativeChangeValue(state); + return { + schema: parsed.schema, + minimum_runtime_version: parsed.minimum_runtime_version, + revision: parsed.revision, + name: parsed.name, + language: parsed.language, + phase: parsed.phase, + brief: parsed.brief, + approval: parsed.approval, + spec_changes: parsed.spec_changes.map((change) => ({ + capability: change.capability, + operation: change.operation, + ...change.source ? { source: change.source } : {}, + base_hash: change.base_hash + })), + verification_result: parsed.verification_result, + verification_report: parsed.verification_report, + archived: parsed.archived, + created_at: parsed.created_at, + run_id: parsed.run_id + }; +} +function nativeChangeDir(paths, name) { + assertNativeName(name); + const target = path16.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, target)) throw new Error("Native change path escaped"); + return target; +} +async function hasPendingNativeSchemaMigration(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function hasPendingNativeCheckpointRecovery(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), "runtime", "checkpoint-journal.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function createNativeChange(options) { + return withNativeMutationLock( + options.paths, + `create change ${options.name}`, + () => createNativeChangeLocked(options) + ); +} +async function createNativeChangeLocked(options) { + assertNativeName(options.name); + const changeDir = nativeChangeDir(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, changeDir); + let createdChangeDir = false; + try { + try { + await fs13.mkdir(changeDir, { recursive: false }); + createdChangeDir = true; + } catch (error) { + if (error.code === "ENOENT") { + await fs13.mkdir(options.paths.changesDir, { recursive: true }); + try { + await fs13.mkdir(changeDir, { recursive: false }); + createdChangeDir = true; + } catch (retryError) { + if (retryError.code === "EEXIST") { + throw new Error(`Native change already exists: ${options.name}`, { + cause: retryError + }); + } + throw retryError; + } + } else if (error.code === "EEXIST") { + throw new Error(`Native change already exists: ${options.name}`, { cause: error }); + } else { + throw error; + } + } + const state = { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + name: options.name, + language: options.language, + phase: "shape", + brief: "brief.md", + approval: null, + approved_contract_hash: null, + spec_changes: [], + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: false, + created_at: (options.now ?? /* @__PURE__ */ new Date()).toISOString().slice(0, 10), + run_id: null + }; + await Promise.all([ + fs13.mkdir(path16.join(changeDir, "specs"), { recursive: true }), + fs13.mkdir(path16.join(changeDir, "runtime", "checkpoints"), { recursive: true }), + atomicWriteText(path16.join(changeDir, "brief.md"), NATIVE_BRIEF_TEMPLATE) + ]); + const projectConfig = await readProjectConfig(options.paths.projectRoot); + const snapshot2 = projectConfig?.native.snapshot ?? DEFAULT_NATIVE_SNAPSHOT_CONFIG; + const baseline = await createNativeContentSnapshot(options.paths, { + now: options.now, + origin: "change-created", + policy: snapshot2, + limits: { + maxFiles: snapshot2.max_files, + maxFileBytes: snapshot2.max_total_bytes, + maxTotalBytes: snapshot2.max_total_bytes, + maxDurationMs: snapshot2.max_duration_ms + }, + deadlineMs: snapshot2.max_duration_ms + }); + if (!baseline.complete) { + const health = inspectNativeContentSnapshotHealth(baseline); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + throw new NativeBaselineIncompleteError( + state.name, + baseline.omittedCount, + omittedByReason, + health.samplePaths, + health.sampleTruncated, + baseline.limits, + baseline.policy?.hash ?? null + ); + } + await writeNativeBaselineManifest(options.paths, state.name, baseline); + await createNativeChangeFile(options.paths, state); + await writeNativeWorkspaceIdentity({ + paths: options.paths, + name: state.name, + revision: state.revision, + now: options.now + }); + return state; + } catch (error) { + if (createdChangeDir) await fs13.rm(changeDir, { recursive: true, force: true }); + throw error; + } +} +var NATIVE_CHANGE_DOCUMENT_MAX_BYTES = 256 * 1024; +async function readChangeDocumentFile(file, root = path16.dirname(file)) { + const ref = path16.relative(root, file).split(path16.sep).join("/"); + const source = await readNativeBoundedTextFile({ + root, + ref, + maxBytes: NATIVE_CHANGE_DOCUMENT_MAX_BYTES + }); + const document = (0, import_yaml3.parseDocument)(source.text, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid Native change file ${file}: ${document.errors[0].message}`); + } + return document.toJS(); +} +async function inspectNativeChange(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + const inspection = inspectNativeChangeValue(await readChangeDocumentFile(file, paths.nativeRoot)); + if (inspection.state && inspection.state.name !== name) { + throw new Error(`Native change directory/name mismatch: ${name}`); + } + if (await hasPendingNativeSchemaMigration(paths, name)) { + return { + status: "migration-required", + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + state: inspection.state, + message: `Native schema migration is incomplete for ${name}; run doctor --repair` + }; + } + return inspection; +} +async function readNativeChange(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + throw new NativeSchemaMigrationRequiredError(name, inspection.schema); + } + if (inspection.status === "runtime-incompatible" || !inspection.state) { + throw new NativeRuntimeCompatibilityError(inspection.schema, inspection.minimumRuntimeVersion); + } + return inspection.state; +} +async function createNativeChangeFile(paths, state) { + const file = path16.join(nativeChangeDir(paths, state.name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.access(file); + throw new Error(`Native change state already exists: ${state.name}`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + if (state.revision !== 1) throw new Error("New Native change must start at revision 1"); + await atomicWriteText(file, (0, import_yaml3.stringify)(nativeChangeDocument(state))); +} +async function compareAndSwapNativeChangeFile(file, state, expectedRevision) { + const next = { + ...state, + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: expectedRevision + 1 + }; + const result2 = await compareAndSwapNativeRevision({ + expectedRevision, + next, + read: async () => { + const current = parseNativeChangeValue(await readChangeDocumentFile(file)); + if (current.name !== state.name) { + throw new Error(`Native change file/name mismatch: ${state.name}`); + } + return current; + }, + write: (value) => atomicWriteText(file, (0, import_yaml3.stringify)(nativeChangeDocument(value))), + equals: (left, right) => JSON.stringify(nativeChangeDocument(left)) === JSON.stringify(nativeChangeDocument(right)), + conflict: (actualRevision) => new NativeChangeRevisionConflictError(state.name, expectedRevision, actualRevision) + }); + Object.assign(state, result2); + return result2; +} +async function compareAndSwapNativeChangeLocked(paths, state, expectedRevision, options) { + await assertNoPendingNativeRootMove(paths.projectRoot); + if (await hasPendingNativeSchemaMigration(paths, state.name)) { + throw new NativeSchemaMigrationRequiredError(state.name, state.schema); + } + if (!options?.allowPendingCheckpointRecovery && await hasPendingNativeCheckpointRecovery(paths, state.name)) { + throw new Error( + `Native progress checkpoint recovery is required for ${state.name} before another state write` + ); + } + await assertNativeTrajectoryHealthy(paths, state.name); + const file = path16.join(nativeChangeDir(paths, state.name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + return compareAndSwapNativeChangeFile(file, state, expectedRevision); +} +async function writeNativeChangeFile(file, state) { + return compareAndSwapNativeChangeFile(file, state, state.revision); +} +async function readNativeChangeFile(file) { + return parseNativeChangeValue(await readChangeDocumentFile(file)); +} + +// domains/comet-native/native-artifacts.ts +var BRIEF_REQUIRED = ["Outcome", "Scope", "Non-goals", "Acceptance examples"]; +var BRIEF_ALL = [ + ...BRIEF_REQUIRED, + "Constraints and invariants", + "Decisions", + "Open questions", + "Verification expectations" +]; +var VERIFICATION_ALL = [ + "Acceptance evidence", + "Commands and results", + "Skipped checks", + "Spec consistency", + "Known limitations and risks", + "Conclusion" +]; +var NATIVE_ARTIFACT_VALIDATION_LIMITS = { + maxFileBytes: DEFAULT_NATIVE_ARTIFACT_MAX_BYTES +}; +function markdownSections(source) { + const sections = /* @__PURE__ */ new Map(); + let heading = null; + let body = []; + const flush = () => { + if (heading !== null) sections.set(heading, body.join("\n").trim()); + }; + for (const line of source.split(/\r?\n/u)) { + const match = /^# ([^#].*)$/u.exec(line); + if (match) { + flush(); + heading = match[1].trim(); + body = []; + } else if (heading !== null) { + body.push(line); + } + } + flush(); + return sections; +} +async function readContainedFile(root, relativeRef) { + const target = path17.resolve(root, ...relativeRef.split(/[\\/]/u)); + if (!isInsidePath(root, target)) + throw new Error(`Artifact escapes Native change: ${relativeRef}`); + const realRoot = await fs14.realpath(root); + const realTarget = await fs14.realpath(target); + if (!isInsidePath(realRoot, realTarget)) { + throw new Error(`Artifact symlink escapes Native change: ${relativeRef}`); + } + if (!(await fs14.stat(realTarget)).isFile()) + throw new Error(`Artifact is not a file: ${relativeRef}`); + return realTarget; +} +function result(findings) { + return { valid: findings.length === 0, findings }; +} +async function validateNativeBrief(changeDir, briefRef) { + const findings = []; + let source; + try { + source = (await readNativeBoundedTextFile({ + root: changeDir, + ref: briefRef, + maxBytes: NATIVE_ARTIFACT_VALIDATION_LIMITS.maxFileBytes + })).text; + } catch (error) { + return result([{ code: "brief-missing", message: error.message, path: briefRef }]); + } + const sections = markdownSections(source); + for (const heading of BRIEF_ALL) { + if (!sections.has(heading)) { + findings.push({ + code: "brief-section-missing", + message: `Missing brief section: ${heading}`, + path: briefRef + }); + } + } + for (const heading of BRIEF_REQUIRED) { + if ((sections.get(heading) ?? "").length === 0) { + findings.push({ + code: "brief-section-empty", + message: `Brief section is empty: ${heading}`, + path: briefRef + }); + } + } + const openQuestions = sections.get("Open questions") ?? ""; + if (/^\s*-\s*\[blocking\]/imu.test(openQuestions)) { + findings.push({ + code: "brief-blocking-question", + message: "Brief has a blocking open question", + path: briefRef + }); + } + return result(findings); +} +async function validateNativeVerification(changeDir, reportRef) { + const findings = []; + let source; + try { + source = (await readNativeBoundedTextFile({ + root: changeDir, + ref: reportRef, + maxBytes: NATIVE_ARTIFACT_VALIDATION_LIMITS.maxFileBytes + })).text; + } catch (error) { + return result([ + { code: "verification-missing", message: error.message, path: reportRef } + ]); + } + const sections = markdownSections(source); + for (const heading of VERIFICATION_ALL) { + if (!sections.has(heading)) { + findings.push({ + code: "verification-section-missing", + message: `Missing verification section: ${heading}`, + path: reportRef + }); + } else if ((sections.get(heading) ?? "").length === 0) { + findings.push({ + code: "verification-section-empty", + message: `Verification section is empty: ${heading}`, + path: reportRef + }); + } + } + return result(findings); +} +function canonicalSpecPath(paths, capability) { + return path17.join(paths.specsDir, capability, "spec.md"); +} +async function validateNativeSpecChanges(paths, state) { + const findings = []; + const changeDir = nativeChangeDir(paths, state.name); + for (const change of state.spec_changes) { + const canonical = canonicalSpecPath(paths, change.capability); + let canonicalHash2 = null; + try { + await resolveContainedNativePath(paths.nativeRoot, canonical); + canonicalHash2 = await sha256File(canonical); + } catch (error) { + if (error.code !== "ENOENT") { + findings.push({ + code: "spec-canonical-unsafe", + message: error.message, + path: canonical + }); + continue; + } + } + if (change.operation === "create" && canonicalHash2 !== null) { + findings.push({ + code: "spec-create-exists", + message: `Canonical spec already exists: ${change.capability}`, + path: canonical + }); + } + if (change.operation !== "create" && canonicalHash2 === null) { + findings.push({ + code: "spec-base-missing", + message: `Canonical spec is missing: ${change.capability}`, + path: canonical + }); + } + if (change.operation !== "create" && canonicalHash2 !== null && canonicalHash2 !== change.base_hash) { + findings.push({ + code: "spec-base-conflict", + message: `Canonical spec changed for ${change.capability}: expected ${change.base_hash}, actual ${canonicalHash2}`, + path: canonical + }); + } + if (change.source) { + try { + await readContainedFile(changeDir, change.source); + } catch (error) { + findings.push({ + code: "spec-source-invalid", + message: error.message, + path: change.source + }); + } + } + } + return result(findings); +} +async function resolveNativeArtifactFile(changeDir, relativeRef) { + return readContainedFile(changeDir, relativeRef); +} + +// domains/comet-native/native-archive-inspection.ts +import { promises as fs18 } from "node:fs"; +import path29 from "node:path"; + +// domains/comet-native/native-archive-preflight.ts +import path18 from "node:path"; + +// domains/comet-native/native-canonical-hash.ts +import { createHash as createHash9 } from "crypto"; +function invalidCanonicalJson(detail) { + throw new TypeError(`Value is not valid canonical JSON: ${detail}`); +} +function canonicalArray(value, ancestors) { + if (ancestors.has(value)) invalidCanonicalJson("cyclic structures are not supported"); + ancestors.add(value); + try { + const enumerableKeys = Object.keys(value); + for (let index = 0; index < value.length; index += 1) { + if (!Object.prototype.hasOwnProperty.call(value, index)) { + invalidCanonicalJson("sparse arrays are not supported"); + } + } + if (enumerableKeys.length !== value.length || enumerableKeys.some((key, index) => key !== String(index))) { + invalidCanonicalJson("arrays must not have named enumerable properties"); + } + if (Object.getOwnPropertySymbols(value).length > 0) { + invalidCanonicalJson("symbol properties are not supported"); + } + return `[${value.map((entry2) => canonicalValue(entry2, ancestors)).join(",")}]`; + } finally { + ancestors.delete(value); + } +} +function canonicalObject(value, ancestors) { + const prototype = Object.getPrototypeOf(value); + if (prototype !== Object.prototype && prototype !== null) { + invalidCanonicalJson("only plain objects are supported"); + } + if (ancestors.has(value)) invalidCanonicalJson("cyclic structures are not supported"); + if (Object.getOwnPropertySymbols(value).length > 0) { + invalidCanonicalJson("symbol properties are not supported"); + } + ancestors.add(value); + try { + const descriptors = Object.getOwnPropertyDescriptors(value); + const keys = Object.keys(value).sort(); + const fields = keys.map((key) => { + const descriptor = descriptors[key]; + if (!descriptor || !("value" in descriptor)) { + invalidCanonicalJson("accessor properties are not supported"); + } + return `${JSON.stringify(key)}:${canonicalValue(descriptor.value, ancestors)}`; + }); + return `{${fields.join(",")}}`; + } finally { + ancestors.delete(value); + } +} +function canonicalValue(value, ancestors) { + if (value === null) return "null"; + switch (typeof value) { + case "boolean": + return value ? "true" : "false"; + case "string": + return JSON.stringify(value); + case "number": + if (!Number.isFinite(value)) invalidCanonicalJson("numbers must be finite"); + return JSON.stringify(Object.is(value, -0) ? 0 : value); + case "object": + return Array.isArray(value) ? canonicalArray(value, ancestors) : canonicalObject(value, ancestors); + case "bigint": + case "function": + case "symbol": + case "undefined": + return invalidCanonicalJson(`${typeof value} values are not supported`); + } + return invalidCanonicalJson("unsupported value type"); +} +function canonicalJson(value) { + return canonicalValue(value, /* @__PURE__ */ new Set()); +} +function canonicalHash(tag, value) { + if (tag.length === 0) throw new TypeError("Canonical hash tag must be non-empty"); + if (/[\r\n]/u.test(tag)) { + throw new TypeError("Canonical hash tag must not contain a line break"); + } + return createHash9("sha256").update(`${tag} +${canonicalJson(value)}`).digest("hex"); +} + +// domains/comet-native/native-archive-preflight.ts +var NATIVE_ARCHIVE_PREFLIGHT_SCHEMA = "comet.native.archive-preflight.v1"; +var PREFLIGHT_HASH_TAG = "comet.native.archive-preflight.v1"; +var OPERATION_HASH_TAG = "comet.native.archive-operation.v1"; +var HASH_PATTERN4 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN2 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var FINDING_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +function compareText(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function hash(value, label) { + if (typeof value !== "string" || !HASH_PATTERN4.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function optionalHash(value, label) { + return value === null ? null : hash(value, label); +} +function normalizedRef(value, label) { + const normalized = path18.posix.normalize(value); + if (typeof value !== "string" || value.length === 0 || value !== value.trim() || value.includes("\\") || path18.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized Native-relative ref`); + } + return value; +} +function positiveRevision(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native archive preflight revision must be a positive integer"); + } + return value; +} +function normalizeSpec(value, index) { + if (!NAME_PATTERN2.test(value.capability)) { + throw new Error(`Native archive spec ${index} capability is invalid`); + } + if (value.operation !== "create" && value.operation !== "replace" && value.operation !== "remove") { + throw new Error(`Native archive spec ${value.capability} operation is invalid`); + } + const expectedBaseHash = optionalHash( + value.expectedBaseHash, + `Native archive spec ${value.capability} expected base` + ); + const actualBaseHash = optionalHash( + value.actualBaseHash, + `Native archive spec ${value.capability} actual base` + ); + const proposedHash = optionalHash( + value.proposedHash, + `Native archive spec ${value.capability} proposed content` + ); + if (value.operation === "create" && expectedBaseHash !== null) { + throw new Error(`Native archive create ${value.capability} must expect no canonical base`); + } + if (value.operation !== "create" && expectedBaseHash === null) { + throw new Error(`Native archive ${value.operation} ${value.capability} requires a base hash`); + } + if (value.operation === "remove" !== (proposedHash === null)) { + throw new Error( + `Native archive ${value.operation} ${value.capability} proposed hash is invalid` + ); + } + return { + capability: value.capability, + operation: value.operation, + expectedBaseHash, + actualBaseHash, + proposedHash + }; +} +function normalizeEvidence(value) { + if (value.result !== "pending" && value.result !== "pass" && value.result !== "fail") { + throw new Error("Native archive verification result is invalid"); + } + if (!["missing", "invalid", "stale", "complete", "partial"].includes(value.freshness)) { + throw new Error("Native archive verification freshness is invalid"); + } + if (!Number.isSafeInteger(value.skippedAcceptanceCount) || value.skippedAcceptanceCount < 0) { + throw new Error("Native archive skipped acceptance count is invalid"); + } + const normalized = { + result: value.result, + freshness: value.freshness, + contractHash: optionalHash(value.contractHash, "Native archive contract hash"), + acceptanceHash: optionalHash(value.acceptanceHash, "Native archive acceptance hash"), + implementationScopeHash: optionalHash( + value.implementationScopeHash, + "Native archive implementation scope hash" + ), + reportHash: optionalHash(value.reportHash, "Native archive report hash"), + envelopeHash: optionalHash(value.envelopeHash, "Native archive envelope hash"), + partialAllowanceHash: optionalHash( + value.partialAllowanceHash, + "Native archive partial allowance hash" + ), + skippedAcceptanceCount: value.skippedAcceptanceCount + }; + if ((value.freshness === "complete" || value.freshness === "partial") && [ + normalized.contractHash, + normalized.acceptanceHash, + normalized.implementationScopeHash, + normalized.reportHash, + normalized.envelopeHash + ].some((entry2) => entry2 === null)) { + throw new Error("Fresh Native archive evidence requires every bound content hash"); + } + if (value.freshness === "partial" && normalized.partialAllowanceHash === null || value.freshness === "complete" && normalized.partialAllowanceHash !== null) { + throw new Error("Native archive partial evidence allowance state is invalid"); + } + return normalized; +} +function normalizeFindingCodes(values) { + if (values.length > 64) throw new Error("Native archive preflight has too many findings"); + const codes = values.map((value) => { + if (typeof value !== "string" || !FINDING_PATTERN.test(value)) { + throw new Error(`Native archive preflight finding code is invalid: ${String(value)}`); + } + return value; + }); + const normalized = [...new Set(codes)].sort(compareText); + if (normalized.length !== codes.length) { + throw new Error("Native archive preflight has duplicate finding codes"); + } + return normalized; +} +function derivedFindings(input) { + const findings = []; + if (input.phase !== "archive") findings.push("archive-phase-required"); + if (input.archived) findings.push("change-already-archived"); + if (input.pendingJournal) findings.push("pending-journal"); + if (input.targetExists) findings.push("archive-target-exists"); + if (input.evidence.result !== "pass") findings.push("verification-not-passed"); + if (input.evidence.freshness === "missing") findings.push("verification-evidence-missing"); + if (input.evidence.freshness === "invalid") findings.push("verification-evidence-invalid"); + if (input.evidence.freshness === "stale") findings.push("verification-evidence-stale"); + for (const spec of input.specs) { + if (spec.actualBaseHash !== spec.expectedBaseHash) findings.push("spec-base-conflict"); + } + return [...new Set(findings)].sort(compareText); +} +function buildNativeArchivePreflight(input) { + if (!NAME_PATTERN2.test(input.change)) throw new Error("Native archive change name is invalid"); + if (typeof input.stateSchema !== "string" || input.stateSchema.length === 0) { + throw new Error("Native archive state schema is invalid"); + } + if (!Array.isArray(input.specs) || input.specs.length > 64) { + throw new Error("Native archive preflight exceeds its spec budget"); + } + const specs = input.specs.map(normalizeSpec).sort((left, right) => compareText(left.capability, right.capability)); + if (new Set(specs.map((spec) => spec.capability)).size !== specs.length) { + throw new Error("Native archive preflight contains duplicate capabilities"); + } + const operations = specs.map((spec) => ({ + ...spec, + operationHash: canonicalHash(OPERATION_HASH_TAG, spec) + })); + const evidence = normalizeEvidence(input.evidence); + const findingCodes = [ + .../* @__PURE__ */ new Set([ + ...normalizeFindingCodes(input.findingCodes ?? []), + ...derivedFindings({ + phase: input.phase, + archived: input.archived, + pendingJournal: input.pendingJournal, + targetExists: input.targetExists, + specs, + evidence + }) + ]) + ].sort(compareText); + const revision = positiveRevision(input.revision); + const targetRef = normalizedRef(input.targetRef, "Native archive target"); + const facts = { + stateSchema: input.stateSchema, + change: input.change, + revision, + phase: input.phase, + archived: input.archived, + pendingJournal: input.pendingJournal, + targetRef, + targetExists: input.targetExists, + operations, + evidence, + findingCodes + }; + return { + schema: NATIVE_ARCHIVE_PREFLIGHT_SCHEMA, + change: input.change, + revision, + targetRef, + ready: findingCodes.length === 0, + evidenceFreshness: evidence.freshness, + operationCount: operations.length, + operations, + findingCodes, + preflightHash: canonicalHash(PREFLIGHT_HASH_TAG, facts) + }; +} + +// domains/comet-native/native-conflict-radar.ts +import { createHash as createHash10 } from "node:crypto"; +import path19 from "node:path"; +var NATIVE_CONFLICT_RADAR_SCHEMA = "comet.native.conflict-radar.v1"; +var NATIVE_CONFLICT_RADAR_LIMITS = Object.freeze({ + maxChanges: 32, + maxSpecsPerChange: 64, + maxArtifactsPerChange: 128, + maxTotalSpecs: 512, + maxTotalArtifacts: 1024, + maxSignalsPerRelationship: 8, + maxRelationships: 256, + maxSerializedBytes: 64 * 1024, + maxNameBytes: 128, + maxCapabilityBytes: 128, + maxArtifactPathBytes: 512 +}); +var RADAR_HASH_TAG = "comet.native.conflict-radar.v1"; +var SIGNAL_SET_HASH_TAG = "comet.native.conflict-radar-signals.v1"; +var HASH_PATTERN5 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN3 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CHANGE_KEYS2 = /* @__PURE__ */ new Set([ + "name", + "revision", + "specs", + "declaredArtifacts", + "workspaceIdentityHash" +]); +var SPEC_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "baseHash"]); +var ARTIFACT_KEYS = /* @__PURE__ */ new Set(["path", "kind"]); +function compareText2(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function byteLength(value) { + return Buffer.byteLength(value, "utf8"); +} +function strictRecord(value, keys, label) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + const record8 = value; + const unknown = Object.keys(record8).filter((key) => !keys.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + return record8; +} +function boundedText(value, label, maxBytes) { + if (typeof value !== "string" || value.length === 0 || value !== value.trim()) { + throw new Error(`${label} must be a non-empty trimmed string`); + } + if (byteLength(value) > maxBytes) throw new Error(`${label} exceeds its byte budget`); + return value; +} +function normalizedProjectPath(value, label) { + const candidate = boundedText(value, label, NATIVE_CONFLICT_RADAR_LIMITS.maxArtifactPathBytes); + const normalized = path19.posix.normalize(candidate); + if (candidate.includes("\\") || Array.from(candidate).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || /^(?:[A-Za-z]:|~)/u.test(candidate) || path19.posix.isAbsolute(normalized) || candidate.split("/").includes("..") || normalized !== candidate || normalized === "." || candidate.endsWith("/")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return candidate; +} +function normalizedHash(value, label) { + if (typeof value !== "string" || !HASH_PATTERN5.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function normalizeSpec2(value, label) { + const record8 = strictRecord(value, SPEC_KEYS, label); + const capability = boundedText( + record8.capability, + `${label} capability`, + NATIVE_CONFLICT_RADAR_LIMITS.maxCapabilityBytes + ); + if (!NAME_PATTERN3.test(capability)) throw new Error(`${label} capability is invalid`); + if (record8.operation !== "create" && record8.operation !== "replace" && record8.operation !== "remove") { + throw new Error(`${label} operation is invalid`); + } + const baseHash = record8.baseHash === null ? null : normalizedHash(record8.baseHash, `${label} canonical base hash`); + if (record8.operation === "create" && baseHash !== null) { + throw new Error(`${label} create operation requires a null canonical base hash`); + } + if (record8.operation !== "create" && baseHash === null) { + throw new Error(`${label} ${record8.operation} operation requires a canonical base hash`); + } + return { capability, operation: record8.operation, baseHash }; +} +function normalizeArtifact(value, label) { + const record8 = strictRecord(value, ARTIFACT_KEYS, label); + if (record8.kind !== "file" && record8.kind !== "directory") { + throw new Error(`${label} kind is invalid`); + } + return { path: normalizedProjectPath(record8.path, `${label} path`), kind: record8.kind }; +} +function normalizeChange(value, index) { + const record8 = strictRecord(value, CHANGE_KEYS2, `Conflict radar change ${index}`); + const name = boundedText( + record8.name, + `Conflict radar change ${index} name`, + NATIVE_CONFLICT_RADAR_LIMITS.maxNameBytes + ); + if (!NAME_PATTERN3.test(name)) throw new Error(`Conflict radar change ${index} name is invalid`); + if (!Number.isSafeInteger(record8.revision) || record8.revision < 1) { + throw new Error(`Conflict radar change ${name} revision must be a positive safe integer`); + } + if (!Array.isArray(record8.specs)) { + throw new Error(`Conflict radar change ${name} specs must be an array`); + } + if (record8.specs.length > NATIVE_CONFLICT_RADAR_LIMITS.maxSpecsPerChange) { + throw new Error(`Conflict radar change ${name} exceeds the spec budget`); + } + if (!Array.isArray(record8.declaredArtifacts)) { + throw new Error(`Conflict radar change ${name} declared artifacts must be an array`); + } + if (record8.declaredArtifacts.length > NATIVE_CONFLICT_RADAR_LIMITS.maxArtifactsPerChange) { + throw new Error(`Conflict radar change ${name} exceeds the declared artifact budget`); + } + const specs = record8.specs.map( + (spec, specIndex) => normalizeSpec2(spec, `Conflict radar change ${name} spec ${specIndex}`) + ).sort((left, right) => compareText2(left.capability, right.capability)); + if (new Set(specs.map((spec) => spec.capability)).size !== specs.length) { + throw new Error(`Conflict radar change ${name} has duplicate capabilities`); + } + const declaredArtifacts = record8.declaredArtifacts.map( + (artifact, artifactIndex) => normalizeArtifact(artifact, `Conflict radar change ${name} artifact ${artifactIndex}`) + ).sort( + (left, right) => compareText2(left.path, right.path) || compareText2(left.kind, right.kind) + ); + const artifactPaths = declaredArtifacts.map((artifact) => artifact.path); + if (new Set(artifactPaths).size !== artifactPaths.length) { + throw new Error(`Conflict radar change ${name} has duplicate or conflicting artifact paths`); + } + const workspaceIdentityHash = record8.workspaceIdentityHash === void 0 || record8.workspaceIdentityHash === null ? null : normalizedHash( + record8.workspaceIdentityHash, + `Conflict radar change ${name} workspace identity hash` + ); + return { + name, + revision: record8.revision, + specs, + declaredArtifacts, + workspaceIdentityHash + }; +} +function normalizeChanges(input) { + if (!Array.isArray(input)) throw new Error("Native conflict radar input must be an array"); + if (input.length > NATIVE_CONFLICT_RADAR_LIMITS.maxChanges) { + throw new Error("Native conflict radar exceeds the change budget"); + } + const changes = input.map(normalizeChange).sort((left, right) => compareText2(left.name, right.name)); + if (new Set(changes.map((change) => change.name)).size !== changes.length) { + throw new Error("Native conflict radar contains duplicate change names"); + } + const specCount = changes.reduce((total, change) => total + change.specs.length, 0); + if (specCount > NATIVE_CONFLICT_RADAR_LIMITS.maxTotalSpecs) { + throw new Error("Native conflict radar exceeds the total spec budget"); + } + const artifactCount = changes.reduce( + (total, change) => total + change.declaredArtifacts.length, + 0 + ); + if (artifactCount > NATIVE_CONFLICT_RADAR_LIMITS.maxTotalArtifacts) { + throw new Error("Native conflict radar exceeds the total declared artifact budget"); + } + return changes; +} +function capabilitySignal(left, right) { + const sameCanonicalBase = left.baseHash !== null && left.baseHash === right.baseHash; + const certainty = left.operation === "create" || right.operation === "create" || sameCanonicalBase ? "definite-conflict" : "possible-overlap"; + return { + kind: "capability", + certainty, + capability: left.capability, + leftOperation: left.operation, + rightOperation: right.operation, + leftBaseHash: left.baseHash, + rightBaseHash: right.baseHash + }; +} +function artifactOverlap(left, right) { + const leftOwnsRight = left.kind === "directory" && (right.path === left.path || right.path.startsWith(`${left.path}/`)); + const rightOwnsLeft = right.kind === "directory" && (left.path === right.path || left.path.startsWith(`${right.path}/`)); + const sameFile = left.kind === "file" && right.kind === "file" && left.path === right.path; + if (!leftOwnsRight && !rightOwnsLeft && !sameFile) return null; + return { + kind: "artifact", + certainty: sameFile ? "definite-conflict" : "possible-overlap", + leftArtifact: left, + rightArtifact: right + }; +} +function workspaceRelationship(left, right) { + if (left.workspaceIdentityHash === null || right.workspaceIdentityHash === null) return "unknown"; + return left.workspaceIdentityHash === right.workspaceIdentityHash ? "same" : "different"; +} +function relationship(left, right) { + const visibleSignals = []; + const signalHasher = createHash10("sha256").update(`${SIGNAL_SET_HASH_TAG} +[`); + let signalCount = 0; + let hasDefiniteConflict = false; + const addSignal = (signal) => { + if (signalCount > 0) signalHasher.update(","); + signalHasher.update(canonicalJson(signal)); + signalCount += 1; + if (signal.certainty === "definite-conflict") hasDefiniteConflict = true; + if (visibleSignals.length < NATIVE_CONFLICT_RADAR_LIMITS.maxSignalsPerRelationship) { + visibleSignals.push(signal); + } + }; + const rightSpecs = new Map(right.specs.map((spec) => [spec.capability, spec])); + for (const leftSpec of left.specs) { + const rightSpec = rightSpecs.get(leftSpec.capability); + if (rightSpec) addSignal(capabilitySignal(leftSpec, rightSpec)); + } + for (const leftArtifact of left.declaredArtifacts) { + for (const rightArtifact of right.declaredArtifacts) { + const signal = artifactOverlap(leftArtifact, rightArtifact); + if (signal) addSignal(signal); + } + } + const classification = hasDefiniteConflict ? "definite-conflict" : signalCount > 0 ? "possible-overlap" : "disjoint"; + signalHasher.update("]"); + return { + left: left.name, + right: right.name, + classification, + workspaceRelationship: workspaceRelationship(left, right), + signalCount, + signalHash: signalHasher.digest("hex"), + signals: visibleSignals, + signalsTruncated: visibleSignals.length !== signalCount + }; +} +function classificationRank(value) { + if (value === "definite-conflict") return 0; + if (value === "possible-overlap") return 1; + return 2; +} +function compareRelationships(left, right) { + return classificationRank(left.classification) - classificationRank(right.classification) || compareText2(left.left, right.left) || compareText2(left.right, right.right); +} +function snapshot(changes, allRelationships, relationships, radarHash) { + const counts = { + definiteConflict: allRelationships.filter( + ({ classification }) => classification === "definite-conflict" + ).length, + possibleOverlap: allRelationships.filter( + ({ classification }) => classification === "possible-overlap" + ).length, + disjoint: allRelationships.filter(({ classification }) => classification === "disjoint").length + }; + return { + schema: NATIVE_CONFLICT_RADAR_SCHEMA, + workspaceIdentityAdvisoryOnly: true, + changeCount: changes.length, + relationshipCount: allRelationships.length, + counts, + relationships, + relationshipsTruncated: relationships.length !== allRelationships.length, + omittedRelationshipCount: allRelationships.length - relationships.length, + radarHash + }; +} +function buildNativeConflictRadar(input) { + const changes = normalizeChanges(input); + const allRelationships = []; + for (let leftIndex = 0; leftIndex < changes.length; leftIndex += 1) { + for (let rightIndex = leftIndex + 1; rightIndex < changes.length; rightIndex += 1) { + allRelationships.push(relationship(changes[leftIndex], changes[rightIndex])); + } + } + allRelationships.sort(compareRelationships); + const hashRelationships = allRelationships.map((value) => ({ + left: value.left, + right: value.right, + classification: value.classification, + workspaceRelationship: value.workspaceRelationship, + signalCount: value.signalCount, + signalHash: value.signalHash + })); + const radarHash = canonicalHash(RADAR_HASH_TAG, { changes, relationships: hashRelationships }); + let relationships = allRelationships.slice(0, NATIVE_CONFLICT_RADAR_LIMITS.maxRelationships).map((value) => ({ ...value, signals: [...value.signals] })); + let result2 = snapshot(changes, allRelationships, relationships, radarHash); + while (relationships.length > 0 && byteLength(JSON.stringify(result2)) > NATIVE_CONFLICT_RADAR_LIMITS.maxSerializedBytes) { + relationships = relationships.slice(0, -1); + result2 = snapshot(changes, allRelationships, relationships, radarHash); + } + if (byteLength(JSON.stringify(result2)) > NATIVE_CONFLICT_RADAR_LIMITS.maxSerializedBytes) { + throw new Error("Native conflict radar summary exceeds its serialized output budget"); + } + return result2; +} + +// domains/comet-native/native-evidence-storage.ts +import { createHash as createHash12 } from "node:crypto"; +import { promises as fs15 } from "node:fs"; +import path22 from "node:path"; + +// domains/comet-native/native-verification-scope.ts +import { createHash as createHash11 } from "node:crypto"; +import path20 from "path"; +var NATIVE_IMPLEMENTATION_SCOPE_SCHEMA = "comet.native.implementation-scope.v2"; +var NATIVE_SNAPSHOT_PROJECTION_SCHEMA = "comet.native.content-snapshot-projection.v1"; +var SNAPSHOT_PROJECTION_HASH_TAG = "comet.native.content-snapshot-projection.v1"; +var IMPLEMENTATION_SCOPE_HASH_TAG = "comet.native.implementation-scope.v2"; +var UNRESOLVED_SCOPE_ID_TAG = "comet.native.unresolved-scope-id.v1"; +var SCOPE_DETAIL_OVERFLOW_HASH_TAG = "comet.native.scope-detail-overflow.v1"; +var SNAPSHOT_PROJECTION_OVERFLOW_HASH_TAG = "comet.native.snapshot-projection-overflow.v2"; +var SHA256_HASH_PATTERN = /^[a-f0-9]{64}$/u; +var MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES = 1024 * 1024; +var MAX_NATIVE_DETAILED_SCOPE_CHANGES = 128; +var MAX_NATIVE_DETAILED_UNRESOLVED_SCOPES = 128; +function compareText3(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function nativeSnapshotProjectionRef(hash6) { + if (!SHA256_HASH_PATTERN.test(hash6)) { + throw new Error("Native snapshot projection hash must be a SHA-256 hash"); + } + return `runtime/evidence/snapshots/${hash6}.json`; +} +function compareNullableText(left, right) { + if (left === right) return 0; + if (left === null) return -1; + if (right === null) return 1; + return compareText3(left, right); +} +function projectRelativePath(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.endsWith("/") || /^[a-zA-Z]:/u.test(value)) { + throw new Error(`${label} must be a normalized project-relative path`); + } + const normalized = path20.posix.normalize(value); + if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../") || path20.posix.isAbsolute(normalized)) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function snapshotOmissionPath(value, label) { + return value === "." ? value : projectRelativePath(value, label); +} +function nonNegativeSafeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative safe integer`); + } + return value; +} +function normalizeEntry(entry2, label) { + if (entry2.type !== "file") throw new Error(`${label} must describe a file`); + if (typeof entry2.hash !== "string" || entry2.hash.length === 0) { + throw new Error(`${label} hash must be non-empty`); + } + return { + path: projectRelativePath(entry2.path, `${label} path`), + hash: entry2.hash, + size: nonNegativeSafeInteger(entry2.size, `${label} size`), + type: "file" + }; +} +function normalizeOmission(omission, label) { + return { + path: snapshotOmissionPath(omission.path, `${label} path`), + size: omission.size === null ? null : nonNegativeSafeInteger(omission.size, `${label} size`), + type: omission.type, + reason: omission.reason + }; +} +function compareEntries(left, right) { + return compareText3(left.path, right.path) || compareText3(left.hash, right.hash) || left.size - right.size; +} +function compareOmissions(left, right) { + return compareText3(left.path, right.path) || compareText3(left.reason, right.reason) || compareText3(left.type, right.type) || (left.size ?? -1) - (right.size ?? -1); +} +function serializedEvidenceBytes(value) { + return Buffer.byteLength(JSON.stringify(value, null, 2) + "\n", "utf8"); +} +function updateFramedHash(hash6, kind, value) { + const encoded = `${kind} +${canonicalJson(value)}`; + hash6.update(`${Buffer.byteLength(encoded, "utf8")}:`); + hash6.update(encoded); +} +function fitSnapshotProjectionBudget(value) { + if (serializedEvidenceBytes(value) <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + return value; + } + const entries = [...value.entries]; + const omitted = [...value.omitted]; + let omittedCount = value.omittedCount; + let overflowCount = value.omissionOverflow?.count ?? 0; + const overflowHash = createHash11("sha256"); + overflowHash.update(`${SNAPSHOT_PROJECTION_OVERFLOW_HASH_TAG} +`); + if (value.omissionOverflow) { + updateFramedHash(overflowHash, "existing-overflow", value.omissionOverflow); + } + const foldOmission = (omission, alreadyCounted) => { + if (!alreadyCounted) omittedCount += 1; + overflowCount += 1; + updateFramedHash(overflowHash, "omission", omission); + }; + const foldEntry = (entry2) => { + omittedCount += 1; + overflowCount += 1; + updateFramedHash(overflowHash, "entry", entry2); + }; + const takeCompactableOmission = () => { + for (let index = omitted.length - 1; index >= 0; index -= 1) { + const omission = omitted[index]; + if (omission.reason === "git-enumeration-limit" || omission.reason === "git-selection-changed" || omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed") { + continue; + } + omitted.splice(index, 1); + return omission; + } + return null; + }; + const candidate = () => ({ + ...value, + complete: omittedCount === 0, + entries, + omitted, + omittedCount, + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${"0".repeat(64)}`, + hash: "0".repeat(64), + count: overflowCount + } + }); + let projection = candidate(); + while (serializedEvidenceBytes(projection) > MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + const compactableOmissionCount = omitted.filter( + (omission) => omission.reason !== "git-enumeration-limit" && omission.reason !== "git-selection-changed" && omission.reason !== "physical-enumeration-limit" && omission.reason !== "physical-selection-changed" + ).length; + if (compactableOmissionCount > 0) { + const removeCount = Math.max(1, Math.ceil(omitted.length / 4)); + for (let removed = 0; removed < removeCount; removed += 1) { + const omission = takeCompactableOmission(); + if (omission === null) break; + foldOmission(omission, true); + } + } else if (entries.length > 0) { + const removeCount = Math.max(1, Math.ceil(entries.length / 4)); + for (const entry2 of entries.splice(-removeCount)) { + foldEntry(entry2); + } + } else { + throw new Error("Native snapshot projection metadata exceeds its evidence byte budget"); + } + projection = candidate(); + } + const digest = overflowHash.digest("hex"); + return { + ...projection, + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${digest}`, + hash: digest, + count: overflowCount + } + }; +} +function snapshotProjection(manifest) { + const parsed = parseNativeContentSnapshotManifest(manifest); + const entries = parsed.entries.map( + (entry2, index) => normalizeEntry(entry2, `Snapshot entry ${index}`) + ); + entries.sort(compareEntries); + if (new Set(entries.map((entry2) => entry2.path)).size !== entries.length) { + throw new Error("Snapshot entries must not contain duplicate paths"); + } + const omitted = parsed.omitted.map( + (omission, index) => normalizeOmission(omission, `Snapshot omission ${index}`) + ); + omitted.sort(compareOmissions); + return fitSnapshotProjectionBudget({ + schema: NATIVE_SNAPSHOT_PROJECTION_SCHEMA, + origin: parsed.origin, + ...parsed.capture ? { capture: parsed.capture } : {}, + complete: parsed.complete, + limits: { + maxFiles: parsed.limits.maxFiles, + maxFileBytes: parsed.limits.maxFileBytes, + maxTotalBytes: parsed.limits.maxTotalBytes, + maxManifestBytes: parsed.limits.maxManifestBytes, + ...parsed.limits.maxDurationMs === void 0 ? {} : { maxDurationMs: parsed.limits.maxDurationMs } + }, + ...parsed.policy ? { policy: parsed.policy } : {}, + entries, + omitted, + omittedCount: parsed.omittedCount, + ...parsed.omissionOverflow ? { + omissionOverflow: { + ref: parsed.omissionOverflow.ref, + hash: parsed.omissionOverflow.hash, + count: parsed.omissionOverflow.count + } + } : {} + }); +} +function normalizeDeclaredArtifacts(artifacts) { + const byPath = /* @__PURE__ */ new Map(); + for (const [index, artifact] of artifacts.entries()) { + if (artifact.kind !== "file" && artifact.kind !== "directory") { + throw new Error(`Declared artifact ${index} kind is invalid`); + } + const normalized = { + path: projectRelativePath(artifact.path, `Declared artifact ${index} path`), + kind: artifact.kind + }; + const existing = byPath.get(normalized.path); + if (existing && existing.kind !== normalized.kind) { + throw new Error(`Declared artifact path has conflicting kinds: ${normalized.path}`); + } + byPath.set(normalized.path, normalized); + } + return [...byPath.values()].sort( + (left, right) => compareText3(left.path, right.path) || compareText3(left.kind, right.kind) + ); +} +function artifactOwnsPath(artifact, changedPath) { + if (artifact.kind === "file") return artifact.path === changedPath; + return changedPath === artifact.path || changedPath.startsWith(`${artifact.path}/`); +} +function fileIdentity(entry2) { + return entry2 ? { hash: entry2.hash, size: entry2.size } : null; +} +function visitDerivedChanges(baseline, current, visitor) { + let beforeIndex = 0; + let afterIndex = 0; + while (beforeIndex < baseline.entries.length || afterIndex < current.entries.length) { + const beforeCandidate = baseline.entries[beforeIndex]; + const afterCandidate = current.entries[afterIndex]; + const order = beforeCandidate === void 0 ? 1 : afterCandidate === void 0 ? -1 : compareText3(beforeCandidate.path, afterCandidate.path); + const before = order <= 0 ? beforeCandidate : void 0; + const after = order >= 0 ? afterCandidate : void 0; + if (order <= 0) beforeIndex += 1; + if (order >= 0) afterIndex += 1; + if (before && after && before.hash === after.hash && before.size === after.size) continue; + if (before && !after && !current.complete) continue; + const changedPath = before?.path ?? after?.path; + if (changedPath === void 0) continue; + visitor({ + path: changedPath, + kind: before ? after ? "modified" : "removed" : "added", + before: fileIdentity(before), + after: fileIdentity(after) + }); + } +} +function materializeChange(core, declaredArtifacts) { + return { + ...core, + attributedTo: declaredArtifacts.filter((artifact) => artifactOwnsPath(artifact, core.path)) + }; +} +function unresolvedScope(identity, reason) { + return { + id: `scope:${canonicalHash(UNRESOLVED_SCOPE_ID_TAG, identity)}`, + kind: identity.kind, + source: identity.source, + path: identity.path, + reason + }; +} +function omissionScopes(source, projection) { + const scopes = projection.omitted.map( + (omission) => unresolvedScope( + { + kind: "snapshot-omission", + source, + path: omission.path, + evidence: { + reason: omission.reason, + size: omission.size, + type: omission.type + } + }, + `${source} snapshot omitted ${omission.path}: ${omission.reason}` + ) + ); + if (!projection.complete) { + scopes.push( + unresolvedScope( + { + kind: "snapshot-incomplete", + source, + path: null, + evidence: { omittedCount: projection.omittedCount } + }, + `${source} snapshot is incomplete` + ) + ); + } + if (projection.omissionOverflow) { + scopes.push( + unresolvedScope( + { + kind: "snapshot-omission-overflow", + source, + path: null, + evidence: { + count: projection.omissionOverflow.count, + hash: projection.omissionOverflow.hash, + ref: projection.omissionOverflow.ref + } + }, + `${source} snapshot has ${projection.omissionOverflow.count} unlisted omissions` + ) + ); + } + return scopes; +} +function omissionSummaryScopes(source, projection) { + return omissionScopes(source, { ...projection, omitted: [] }).filter( + (scope) => scope.kind !== "snapshot-omission" + ); +} +function compareUnresolvedScopes(left, right) { + return compareText3(left.kind, right.kind) || compareText3(left.source, right.source) || compareNullableText(left.path, right.path) || compareText3(left.id, right.id); +} +function uniqueUnresolvedScopes(scopes) { + const byId = new Map(scopes.map((scope) => [scope.id, scope])); + return [...byId.values()].sort(compareUnresolvedScopes); +} +function normalizeGitChangedPaths(paths) { + return [ + ...new Set(paths.map((value, index) => projectRelativePath(value, `Git path ${index}`))) + ].sort(compareText3); +} +function normalizeScopeAuthority(input) { + if (typeof input.contractHash !== "string" || !SHA256_HASH_PATTERN.test(input.contractHash)) { + throw new Error("Contract hash must be a SHA-256 hash"); + } + return { + contractHash: input.contractHash, + declaredArtifacts: normalizeDeclaredArtifacts(input.declaredArtifacts), + noCodeReason: input.noCodeReason?.trim() || null, + ...input.gitChangedPaths === void 0 ? {} : { gitChangedPaths: normalizeGitChangedPaths(input.gitChangedPaths) } + }; +} +function scanScopeChanges(baseline, current, declaredArtifacts, gitChangedPaths) { + const candidates = []; + const gitChangedPathSet = gitChangedPaths === void 0 ? null : new Set(gitChangedPaths); + const gitPathsPresentInChanges = []; + let candidateBytes = 0; + let candidateBudgetExhausted = false; + let totalChanges = 0; + let totalUnattributed = 0; + visitDerivedChanges(baseline, current, (core) => { + const attributed = declaredArtifacts.some((artifact) => artifactOwnsPath(artifact, core.path)); + totalChanges += 1; + if (!attributed) totalUnattributed += 1; + if (gitChangedPathSet?.has(core.path)) gitPathsPresentInChanges.push(core.path); + if (!candidateBudgetExhausted && candidates.length < MAX_NATIVE_DETAILED_SCOPE_CHANGES) { + const candidate = materializeChange(core, declaredArtifacts); + const nextBytes = serializedEvidenceBytes(candidate); + if (candidateBytes + nextBytes <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + candidates.push(candidate); + candidateBytes += nextBytes; + } else { + candidateBudgetExhausted = true; + } + } + }); + return { candidates, gitPathsPresentInChanges, totalChanges, totalUnattributed }; +} +function scopeDetailOverflow(hash6, counts) { + return unresolvedScope( + { + kind: "scope-detail-overflow", + source: "implementation-scope", + path: null, + evidence: { + changeCount: counts.changes, + hash: hash6, + unattributedCount: counts.unattributed, + unresolvedCount: counts.unresolved + } + }, + `Implementation scope summarized ${counts.changes} additional change details and ${counts.unresolved} unresolved details (${counts.unattributed} unattributed); overflow hash ${hash6}` + ); +} +function parseScopeDetailOverflow(scope) { + const match = /^Implementation scope summarized ([0-9]+) additional change details and ([0-9]+) unresolved details \(([0-9]+) unattributed\); overflow hash ([a-f0-9]{64})$/u.exec( + scope.reason + ); + if (!match) throw new Error("Implementation scope detail overflow is invalid"); + const counts = { + changes: nonNegativeSafeInteger(Number(match[1]), "Scope overflow change count"), + unresolved: nonNegativeSafeInteger(Number(match[2]), "Scope overflow unresolved count"), + unattributed: nonNegativeSafeInteger(Number(match[3]), "Scope overflow unattributed count") + }; + const hash6 = scopeHashValue(match[4], "Scope detail overflow hash"); + if (JSON.stringify(scope) !== JSON.stringify(scopeDetailOverflow(hash6, counts))) { + throw new Error("Implementation scope detail overflow is inconsistent"); + } + return { ...counts, hash: hash6 }; +} +function hashScopeDetailOverflow(options) { + const hash6 = createHash11("sha256"); + hash6.update(`${SCOPE_DETAIL_OVERFLOW_HASH_TAG} +`); + let changeIndex = 0; + visitDerivedChanges(options.baseline, options.current, (core) => { + if (changeIndex >= options.detailedChangeCount) { + updateFramedHash(hash6, "change", core); + let attributionCount = 0; + for (const artifact of options.declaredArtifacts) { + if (!artifactOwnsPath(artifact, core.path)) continue; + attributionCount += 1; + updateFramedHash(hash6, "change-attribution", artifact); + } + updateFramedHash(hash6, "change-end", { attributionCount }); + if (attributionCount === 0) { + updateFramedHash(hash6, "unattributed-change", { + after: core.after, + before: core.before, + changeKind: core.kind, + path: core.path + }); + } + } + changeIndex += 1; + }); + let omissionIndex = 0; + for (const [source, projection] of [ + ["baseline", options.baseline], + ["current", options.current] + ]) { + for (const omission of projection.omitted) { + if (omissionIndex >= options.detailedOmissionCount) { + updateFramedHash(hash6, "snapshot-omission", { source, ...omission }); + } + omissionIndex += 1; + } + } + return hash6.digest("hex"); +} +function buildScopeFromProjections(baseline, current, authority) { + const { contractHash, declaredArtifacts, noCodeReason } = authority; + const changeScan = scanScopeChanges( + baseline, + current, + declaredArtifacts, + authority.gitChangedPaths + ); + const baselineProjectionHash = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, baseline); + const currentProjectionHash2 = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, current); + const omissionCandidates = []; + for (const [source, projection] of [ + ["baseline", baseline], + ["current", current] + ]) { + for (const omission of projection.omitted) { + if (omissionCandidates.length >= MAX_NATIVE_DETAILED_UNRESOLVED_SCOPES) break; + omissionCandidates.push( + unresolvedScope( + { + kind: "snapshot-omission", + source, + path: omission.path, + evidence: { + reason: omission.reason, + size: omission.size, + type: omission.type + } + }, + `${source} snapshot omitted ${omission.path}: ${omission.reason}` + ) + ); + } + } + const totalOmissionDetails = baseline.omitted.length + current.omitted.length; + const essentialScopes = [ + ...omissionSummaryScopes("baseline", baseline), + ...omissionSummaryScopes("current", current), + ...changeScan.totalChanges === 0 && noCodeReason === null ? [ + unresolvedScope( + { + kind: "missing-no-code-reason", + source: "implementation-scope", + path: null, + evidence: { + baselineProjectionHash, + currentProjectionHash: currentProjectionHash2 + } + }, + "A non-empty no-code reason is required when the snapshots contain no changes" + ) + ] : [] + ]; + const buildGitAdvisory = () => { + if (authority.gitChangedPaths === void 0) return void 0; + const snapshotChangePaths = new Set(changeScan.gitPathsPresentInChanges); + return { + advisoryOnly: true, + changedPaths: authority.gitChangedPaths, + pathsPresentInSnapshotChanges: authority.gitChangedPaths.filter( + (value) => snapshotChangePaths.has(value) + ), + pathsAbsentFromSnapshotChanges: authority.gitChangedPaths.filter( + (value) => !snapshotChangePaths.has(value) + ) + }; + }; + const buildScopeContent = (options) => { + const changes = changeScan.candidates.slice(0, options.detailedChangeCount); + const unattributed = changes.filter((change) => change.attributedTo.length === 0); + const overflowChanges = changeScan.totalChanges - changes.length; + const overflowUnattributed = changeScan.totalUnattributed - unattributed.length; + const overflowOmissions = totalOmissionDetails - options.detailedOmissionCount; + const overflowUnresolved = overflowUnattributed + overflowOmissions; + const unresolved = [ + ...unattributed.map( + (change) => unresolvedScope( + { + kind: "unattributed-change", + source: "implementation-scope", + path: change.path, + evidence: { + after: change.after, + before: change.before, + changeKind: change.kind + } + }, + `Changed path is not covered by a declared artifact: ${change.path}` + ) + ), + ...omissionCandidates.slice(0, options.detailedOmissionCount), + ...essentialScopes, + ...overflowChanges > 0 || overflowUnresolved > 0 ? [ + scopeDetailOverflow(options.overflowHash, { + changes: overflowChanges, + unattributed: overflowUnattributed, + unresolved: overflowUnresolved + }) + ] : [] + ]; + const unresolvedScopes = uniqueUnresolvedScopes(unresolved); + const gitAdvisory = options.includeGitAdvisory ? buildGitAdvisory() : void 0; + return { + schema: NATIVE_IMPLEMENTATION_SCOPE_SCHEMA, + contractHash, + baselineProjectionRef: nativeSnapshotProjectionRef(baselineProjectionHash), + baselineProjectionHash, + currentProjectionRef: nativeSnapshotProjectionRef(currentProjectionHash2), + currentProjectionHash: currentProjectionHash2, + complete: unresolvedScopes.length === 0, + declaredArtifacts, + changes, + unattributed, + unresolvedScopes, + noCodeReason, + ...gitAdvisory ? { gitAdvisory } : {} + }; + }; + let detailedChangeCount = changeScan.candidates.length; + let detailedOmissionCount = omissionCandidates.length; + let includeGitAdvisory = authority.gitChangedPaths !== void 0; + const placeholderHash = "0".repeat(64); + while (true) { + const candidate = buildScopeContent({ + detailedChangeCount, + detailedOmissionCount, + includeGitAdvisory, + overflowHash: placeholderHash + }); + if (serializedEvidenceBytes({ ...candidate, scopeHash: placeholderHash }) <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + break; + } + if (includeGitAdvisory) { + includeGitAdvisory = false; + continue; + } + if (detailedOmissionCount > 0) { + detailedOmissionCount -= Math.max(1, Math.ceil(detailedOmissionCount / 4)); + continue; + } + if (detailedChangeCount > 0) { + const removable = detailedChangeCount; + detailedChangeCount -= Math.max(1, Math.ceil(removable / 4)); + continue; + } + throw new Error("Native implementation scope metadata exceeds its evidence byte budget"); + } + const hasOverflow = changeScan.totalChanges > detailedChangeCount || totalOmissionDetails > detailedOmissionCount; + const overflowHash = hasOverflow ? hashScopeDetailOverflow({ + baseline, + current, + declaredArtifacts, + detailedChangeCount, + detailedOmissionCount + }) : placeholderHash; + const scopeContent = buildScopeContent({ + detailedChangeCount, + detailedOmissionCount, + includeGitAdvisory, + overflowHash + }); + const scope = { + ...scopeContent, + scopeHash: canonicalHash(IMPLEMENTATION_SCOPE_HASH_TAG, scopeContent) + }; + if (serializedEvidenceBytes(scope) > MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + throw new Error("Native implementation scope exceeded its evidence byte budget after fitting"); + } + return scope; +} +function buildNativeImplementationScopeBundle(input) { + const baseline = snapshotProjection(input.baseline); + const current = snapshotProjection(input.current); + const authority = normalizeScopeAuthority(input); + return { + authority, + baseline, + current, + scope: buildScopeFromProjections(baseline, current, authority) + }; +} +var SCOPE_KEYS = /* @__PURE__ */ new Set([ + "schema", + "contractHash", + "baselineProjectionRef", + "baselineProjectionHash", + "currentProjectionRef", + "currentProjectionHash", + "complete", + "declaredArtifacts", + "changes", + "unattributed", + "unresolvedScopes", + "noCodeReason", + "gitAdvisory", + "scopeHash" +]); +function scopeRecord(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactScopeKeys(value, required, optional, label) { + const allowed = /* @__PURE__ */ new Set([...required, ...optional]); + const unknown = Object.keys(value).filter((key) => !allowed.has(key)); + const missing = required.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function scopeHashValue(value, label) { + if (typeof value !== "string" || !SHA256_HASH_PATTERN.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function snapshotProjectionRefValue(value, hash6, label) { + const expected = nativeSnapshotProjectionRef(hash6); + if (value !== expected) throw new Error(`${label} ref/hash mismatch`); + return expected; +} +function parseNativeSnapshotProjection(value, expectedHash) { + const root = scopeRecord(value, "Native snapshot projection"); + exactScopeKeys( + root, + ["schema", "origin", "complete", "limits", "entries", "omitted", "omittedCount"], + ["capture", "omissionOverflow", "policy"], + "Native snapshot projection" + ); + if (root.schema !== NATIVE_SNAPSHOT_PROJECTION_SCHEMA) { + throw new Error("Native snapshot projection schema is invalid"); + } + const parsedManifest = parseNativeContentSnapshotManifest({ + schema: "comet.native.content-snapshot.v1", + origin: root.origin, + ...root.capture === void 0 ? {} : { capture: root.capture }, + createdAt: "1970-01-01T00:00:00.000Z", + complete: root.complete, + limits: root.limits, + ...root.policy === void 0 ? {} : { policy: root.policy }, + entries: root.entries, + omitted: root.omitted, + omittedCount: root.omittedCount, + ...root.omissionOverflow === void 0 ? {} : { omissionOverflow: root.omissionOverflow } + }); + const projection = snapshotProjection(parsedManifest); + if (canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, root) !== canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, projection)) { + throw new Error("Native snapshot projection is not canonical"); + } + const projectionHash = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, projection); + if (expectedHash !== void 0 && scopeHashValue(expectedHash, "Snapshot projection hash") !== projectionHash) { + throw new Error("Native snapshot projection content hash mismatch"); + } + return projection; +} +function parseDeclaredArtifact(value, index) { + const artifact = scopeRecord(value, `Declared artifact ${index}`); + exactScopeKeys(artifact, ["path", "kind"], [], `Declared artifact ${index}`); + if (artifact.kind !== "file" && artifact.kind !== "directory") { + throw new Error(`Declared artifact ${index} kind is invalid`); + } + return { + path: projectRelativePath(artifact.path, `Declared artifact ${index} path`), + kind: artifact.kind + }; +} +function parseFileIdentity(value, label) { + if (value === null) return null; + const identity = scopeRecord(value, label); + exactScopeKeys(identity, ["hash", "size"], [], label); + return { + hash: scopeHashValue(identity.hash, `${label} hash`), + size: nonNegativeSafeInteger(identity.size, `${label} size`) + }; +} +function parseImplementationChange(value, index) { + const change = scopeRecord(value, `Implementation change ${index}`); + exactScopeKeys( + change, + ["path", "kind", "before", "after", "attributedTo"], + [], + `Implementation change ${index}` + ); + if (change.kind !== "added" && change.kind !== "modified" && change.kind !== "removed") { + throw new Error(`Implementation change ${index} kind is invalid`); + } + if (!Array.isArray(change.attributedTo)) { + throw new Error(`Implementation change ${index} attributedTo must be an array`); + } + const attributedTo = change.attributedTo.map(parseDeclaredArtifact); + const normalizedAttribution = normalizeDeclaredArtifacts(attributedTo); + if (JSON.stringify(attributedTo) !== JSON.stringify(normalizedAttribution)) { + throw new Error(`Implementation change ${index} attribution must be sorted and unique`); + } + const before = parseFileIdentity(change.before, `Implementation change ${index} before`); + const after = parseFileIdentity(change.after, `Implementation change ${index} after`); + if (change.kind === "added" && (before !== null || after === null) || change.kind === "removed" && (before === null || after !== null) || change.kind === "modified" && (before === null || after === null || JSON.stringify(before) === JSON.stringify(after))) { + throw new Error(`Implementation change ${index} before/after state is invalid`); + } + return { + path: projectRelativePath(change.path, `Implementation change ${index} path`), + kind: change.kind, + before, + after, + attributedTo + }; +} +function parseUnresolvedScope(value, index) { + const scope = scopeRecord(value, `Unresolved scope ${index}`); + exactScopeKeys( + scope, + ["id", "kind", "source", "path", "reason"], + [], + `Unresolved scope ${index}` + ); + const kinds = /* @__PURE__ */ new Set([ + "unattributed-change", + "snapshot-omission", + "snapshot-incomplete", + "snapshot-omission-overflow", + "scope-detail-overflow", + "missing-no-code-reason" + ]); + if (typeof scope.kind !== "string" || !kinds.has(scope.kind)) { + throw new Error(`Unresolved scope ${index} kind is invalid`); + } + if (scope.source !== "baseline" && scope.source !== "current" && scope.source !== "implementation-scope") { + throw new Error(`Unresolved scope ${index} source is invalid`); + } + if (typeof scope.id !== "string" || !/^scope:[a-f0-9]{64}$/u.test(scope.id)) { + throw new Error(`Unresolved scope ${index} id is invalid`); + } + if (scope.path !== null && typeof scope.path !== "string") { + throw new Error(`Unresolved scope ${index} path is invalid`); + } + if (typeof scope.reason !== "string" || scope.reason.length === 0 || scope.reason.trim() !== scope.reason) { + throw new Error(`Unresolved scope ${index} reason is invalid`); + } + return { + id: scope.id, + kind: scope.kind, + source: scope.source, + path: scope.path === null ? null : scope.kind === "snapshot-omission" ? snapshotOmissionPath(scope.path, `Unresolved scope ${index} path`) : projectRelativePath(scope.path, `Unresolved scope ${index} path`), + reason: scope.reason + }; +} +function parseSortedPaths(value, label) { + if (!Array.isArray(value) || value.some((entry2) => typeof entry2 !== "string")) { + throw new Error(`${label} must be an array of paths`); + } + const paths = value.map((entry2, index) => projectRelativePath(entry2, `${label} ${index}`)); + const normalized = [...new Set(paths)].sort(compareText3); + if (JSON.stringify(paths) !== JSON.stringify(normalized)) { + throw new Error(`${label} must be sorted and unique`); + } + return paths; +} +function parseNativeImplementationScope(value) { + const root = scopeRecord(value, "Native implementation scope"); + const required = [...SCOPE_KEYS].filter((key) => key !== "gitAdvisory"); + exactScopeKeys(root, required, ["gitAdvisory"], "Native implementation scope"); + if (root.schema !== NATIVE_IMPLEMENTATION_SCOPE_SCHEMA) { + throw new Error("Native implementation scope schema is invalid"); + } + const contractHash = scopeHashValue(root.contractHash, "Implementation scope contractHash"); + const baselineProjectionHash = scopeHashValue( + root.baselineProjectionHash, + "Implementation scope baselineProjectionHash" + ); + const baselineProjectionRef = snapshotProjectionRefValue( + root.baselineProjectionRef, + baselineProjectionHash, + "Implementation scope baseline projection" + ); + const currentProjectionHash2 = scopeHashValue( + root.currentProjectionHash, + "Implementation scope currentProjectionHash" + ); + const currentProjectionRef = snapshotProjectionRefValue( + root.currentProjectionRef, + currentProjectionHash2, + "Implementation scope current projection" + ); + if (typeof root.complete !== "boolean") { + throw new Error("Implementation scope complete flag is invalid"); + } + if (!Array.isArray(root.declaredArtifacts) || !Array.isArray(root.changes) || !Array.isArray(root.unattributed) || !Array.isArray(root.unresolvedScopes)) { + throw new Error("Implementation scope collections are invalid"); + } + const declaredArtifacts = root.declaredArtifacts.map(parseDeclaredArtifact); + if (JSON.stringify(declaredArtifacts) !== JSON.stringify(normalizeDeclaredArtifacts(declaredArtifacts))) { + throw new Error("Implementation scope declared artifacts must be sorted and unique"); + } + const changes = root.changes.map(parseImplementationChange); + const sortedChanges = [...changes].sort((left, right) => compareText3(left.path, right.path)); + if (JSON.stringify(changes) !== JSON.stringify(sortedChanges) || new Set(changes.map((change) => change.path)).size !== changes.length) { + throw new Error("Implementation scope changes must be sorted and unique"); + } + const declaredByIdentity = new Set( + declaredArtifacts.map((artifact) => `${artifact.kind}:${artifact.path}`) + ); + if (changes.some( + (change) => change.attributedTo.some( + (artifact) => !declaredByIdentity.has(`${artifact.kind}:${artifact.path}`) + ) + )) { + throw new Error("Implementation scope change references an undeclared artifact"); + } + if (changes.some( + (change) => JSON.stringify(change.attributedTo) !== JSON.stringify( + declaredArtifacts.filter((artifact) => artifactOwnsPath(artifact, change.path)) + ) + )) { + throw new Error("Implementation scope change attribution is inconsistent"); + } + const unattributed = root.unattributed.map(parseImplementationChange); + const expectedUnattributed = changes.filter((change) => change.attributedTo.length === 0); + if (JSON.stringify(unattributed) !== JSON.stringify(expectedUnattributed)) { + throw new Error("Implementation scope unattributed changes are inconsistent"); + } + const unresolvedScopes = root.unresolvedScopes.map(parseUnresolvedScope); + const detailOverflowScopes = unresolvedScopes.filter( + (scope) => scope.kind === "scope-detail-overflow" + ); + if (detailOverflowScopes.length > 1) { + throw new Error("Implementation scope has multiple detail overflow records"); + } + const detailOverflow = detailOverflowScopes[0] === void 0 ? null : parseScopeDetailOverflow(detailOverflowScopes[0]); + if (detailOverflow && (detailOverflow.changes + detailOverflow.unresolved === 0 || detailOverflow.unattributed > detailOverflow.changes || detailOverflow.unattributed > detailOverflow.unresolved)) { + throw new Error("Implementation scope detail overflow counts are inconsistent"); + } + const noCodeReason = root.noCodeReason; + const expectedDerivedScopes = [ + ...expectedUnattributed.map( + (change) => unresolvedScope( + { + kind: "unattributed-change", + source: "implementation-scope", + path: change.path, + evidence: { + after: change.after, + before: change.before, + changeKind: change.kind + } + }, + `Changed path is not covered by a declared artifact: ${change.path}` + ) + ), + ...changes.length + (detailOverflow?.changes ?? 0) === 0 && noCodeReason === null ? [ + unresolvedScope( + { + kind: "missing-no-code-reason", + source: "implementation-scope", + path: null, + evidence: { baselineProjectionHash, currentProjectionHash: currentProjectionHash2 } + }, + "A non-empty no-code reason is required when the snapshots contain no changes" + ) + ] : [] + ]; + const actualDerivedScopes = unresolvedScopes.filter( + (scope) => scope.kind === "unattributed-change" || scope.kind === "missing-no-code-reason" + ); + if (JSON.stringify(unresolvedScopes) !== JSON.stringify([...unresolvedScopes].sort(compareUnresolvedScopes)) || new Set(unresolvedScopes.map((scope) => scope.id)).size !== unresolvedScopes.length || JSON.stringify(actualDerivedScopes) !== JSON.stringify(expectedDerivedScopes.sort(compareUnresolvedScopes)) || root.complete !== (unresolvedScopes.length === 0)) { + throw new Error("Implementation scope unresolved scopes are inconsistent"); + } + if (root.noCodeReason !== null && (typeof root.noCodeReason !== "string" || root.noCodeReason.length === 0 || root.noCodeReason.trim() !== root.noCodeReason)) { + throw new Error("Implementation scope no-code reason is invalid"); + } + let gitAdvisory; + if (root.gitAdvisory !== void 0) { + const advisory = scopeRecord(root.gitAdvisory, "Implementation scope Git advisory"); + exactScopeKeys( + advisory, + [ + "advisoryOnly", + "changedPaths", + "pathsPresentInSnapshotChanges", + "pathsAbsentFromSnapshotChanges" + ], + [], + "Implementation scope Git advisory" + ); + if (advisory.advisoryOnly !== true) { + throw new Error("Implementation scope Git advisory must remain advisory-only"); + } + const changedPaths = parseSortedPaths(advisory.changedPaths, "Git changed paths"); + const pathsPresentInSnapshotChanges = parseSortedPaths( + advisory.pathsPresentInSnapshotChanges, + "Git present paths" + ); + const pathsAbsentFromSnapshotChanges = parseSortedPaths( + advisory.pathsAbsentFromSnapshotChanges, + "Git absent paths" + ); + const partition = [...pathsPresentInSnapshotChanges, ...pathsAbsentFromSnapshotChanges].sort( + compareText3 + ); + const detailedChangePaths = new Set(changes.map((change) => change.path)); + if (JSON.stringify(partition) !== JSON.stringify(changedPaths) || (detailOverflow?.changes ?? 0) === 0 && pathsPresentInSnapshotChanges.some((entry2) => !detailedChangePaths.has(entry2)) || pathsAbsentFromSnapshotChanges.some((entry2) => detailedChangePaths.has(entry2))) { + throw new Error("Implementation scope Git advisory partition is invalid"); + } + gitAdvisory = { + advisoryOnly: true, + changedPaths, + pathsPresentInSnapshotChanges, + pathsAbsentFromSnapshotChanges + }; + } + const content = { + schema: NATIVE_IMPLEMENTATION_SCOPE_SCHEMA, + contractHash, + baselineProjectionRef, + baselineProjectionHash, + currentProjectionRef, + currentProjectionHash: currentProjectionHash2, + complete: root.complete, + declaredArtifacts, + changes, + unattributed, + unresolvedScopes, + noCodeReason, + ...gitAdvisory ? { gitAdvisory } : {} + }; + const scopeHash = scopeHashValue(root.scopeHash, "Implementation scope scopeHash"); + if (canonicalHash(IMPLEMENTATION_SCOPE_HASH_TAG, content) !== scopeHash) { + throw new Error("Implementation scope content hash mismatch"); + } + return { ...content, scopeHash }; +} +function parseScopeAuthority(value) { + const root = scopeRecord(value, "Native implementation scope authority"); + exactScopeKeys( + root, + ["contractHash", "declaredArtifacts", "noCodeReason"], + ["gitChangedPaths"], + "Native implementation scope authority" + ); + if (!Array.isArray(root.declaredArtifacts)) { + throw new Error("Native implementation scope authority declarations must be an array"); + } + const declaredArtifacts = root.declaredArtifacts.map(parseDeclaredArtifact); + if (JSON.stringify(declaredArtifacts) !== JSON.stringify(normalizeDeclaredArtifacts(declaredArtifacts))) { + throw new Error("Native implementation scope authority declarations must be canonical"); + } + if (root.noCodeReason !== null && (typeof root.noCodeReason !== "string" || root.noCodeReason.length === 0 || root.noCodeReason.trim() !== root.noCodeReason)) { + throw new Error("Native implementation scope authority no-code reason is invalid"); + } + let gitChangedPaths; + if (root.gitChangedPaths !== void 0) { + gitChangedPaths = parseSortedPaths( + root.gitChangedPaths, + "Native implementation scope authority Git paths" + ); + } + const authority = normalizeScopeAuthority({ + contractHash: root.contractHash, + declaredArtifacts, + noCodeReason: root.noCodeReason, + ...gitChangedPaths === void 0 ? {} : { gitChangedPaths } + }); + if (authority.contractHash !== root.contractHash) { + throw new Error("Native implementation scope authority contract hash is not canonical"); + } + return authority; +} +function parseNativeImplementationScopeBundle(value) { + const root = scopeRecord(value, "Native implementation scope bundle"); + exactScopeKeys( + root, + ["authority", "baseline", "current", "scope"], + [], + "Native implementation scope bundle" + ); + const authority = parseScopeAuthority(root.authority); + const baseline = parseNativeSnapshotProjection(root.baseline); + const current = parseNativeSnapshotProjection(root.current); + const suppliedScope = parseNativeImplementationScope(root.scope); + const rebuiltScope = buildScopeFromProjections(baseline, current, authority); + if (JSON.stringify(suppliedScope) !== JSON.stringify(rebuiltScope)) { + throw new Error("Native implementation scope does not match its authoritative bundle"); + } + return { authority, baseline, current, scope: rebuiltScope }; +} +function rebuildNativeImplementationScopeBundle(input) { + const suppliedScope = parseNativeImplementationScope(input.scope); + return parseNativeImplementationScopeBundle({ + authority: { + contractHash: suppliedScope.contractHash, + declaredArtifacts: suppliedScope.declaredArtifacts, + noCodeReason: suppliedScope.noCodeReason, + ...suppliedScope.gitAdvisory ? { gitChangedPaths: suppliedScope.gitAdvisory.changedPaths } : {} + }, + baseline: input.baseline, + current: input.current, + scope: suppliedScope + }); +} + +// domains/comet-native/native-verification-evidence.ts +import path21 from "node:path"; + +// domains/comet-native/native-redaction.ts +var AUTHORIZATION_PATTERN = /\b(Bearer|Basic)\s+[^\s"']+/giu; +var PRIVATE_KEY_PATTERN = /-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/gu; +var URI_CREDENTIAL_PATTERN = /(https?:\/\/)[^\s/@:]+:[^\s/@]+@/giu; +var KNOWN_TOKEN_PATTERN = /\b(?:sk-(?:proj-)?[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{16,}|AKIA[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{20,})\b/gu; +var JSON_CREDENTIAL_PATTERN = /("(?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie)"\s*:\s*)"(?:\\[\s\S]|[^"\\\r\n])*"/giu; +var QUOTED_CREDENTIAL_PATTERN = /(["'])((?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie))\1(\s*:\s*)(["'])[^\r\n]*?\4/giu; +var CREDENTIAL_ASSIGNMENT_PATTERN = /\b((?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie))\b(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;"']+)/giu; +function redactNativeCredentialText(value) { + return value.replace(AUTHORIZATION_PATTERN, "$1 [REDACTED]").replace(PRIVATE_KEY_PATTERN, "[REDACTED PRIVATE KEY]").replace(URI_CREDENTIAL_PATTERN, "$1[REDACTED]@").replace(KNOWN_TOKEN_PATTERN, "[REDACTED TOKEN]").replace(JSON_CREDENTIAL_PATTERN, '$1"[REDACTED]"').replace(QUOTED_CREDENTIAL_PATTERN, "$1$2$1$3$4[REDACTED]$4").replace(CREDENTIAL_ASSIGNMENT_PATTERN, "$1$2[REDACTED]"); +} + +// domains/comet-native/native-verification-evidence.ts +var HASH_PATTERN6 = /^[a-f0-9]{64}$/u; +var MISSING_ACCEPTANCE_DETAIL_LIMIT = 8; +var ACCEPTANCE_TRACE_HASH_TAG = "comet.native.acceptance-trace.v1"; +var PARTIAL_ALLOWANCE_HASH_TAG = "comet.native.partial-allowance.v1"; +var VERIFICATION_ENVELOPE_HASH_TAG = "comet.native.verification-evidence.v1"; +function hash2(value, label) { + if (!HASH_PATTERN6.test(value)) throw new Error(`${label} must be a SHA-256 hash`); + return value; +} +function positiveRevision2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native evidence source revision must be a positive integer"); + } + return value; +} +function changeName(value) { + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(value)) { + throw new Error(`Invalid Native evidence change name: ${value}`); + } + return value; +} +function requiredText(value, label, max = 2e3) { + const normalized = redactNativeCredentialText(value).trim(); + if (normalized.length === 0 || normalized.length > max) { + throw new Error(`${label} must be between 1 and ${max} characters`); + } + return normalized; +} +function portableRef(value, label) { + const normalized = path21.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path21.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized relative ref`); + } + return value; +} +function portableEvidenceRef(value, label, nativeRootRef3) { + const reference = portableRef(value, label); + const sensitiveReason = nativeSensitiveRelativePathReason(reference); + const lowerReference = reference.toLowerCase(); + const lowerNativeRoot = nativeRootRef3 ? portableRef(nativeRootRef3, "Native root ref").toLowerCase() : null; + if (sensitiveReason || lowerReference === "runtime" || lowerReference.startsWith("runtime/") || lowerNativeRoot !== null && (lowerReference === lowerNativeRoot || lowerReference.startsWith(`${lowerNativeRoot}/`))) { + throw new Error( + `${label} is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${reference}` + ); + } + return reference; +} +function checkReceiptRef(value) { + const reference = portableRef(value, "Verification receipt ref"); + if (!/^runtime\/evidence\/check-receipts\/[a-f0-9]{64}\.json$/u.test(reference)) { + throw new Error("Verification receipt ref must identify a Native check receipt"); + } + return reference; +} +function timestamp(value) { + const result2 = value.toISOString(); + if (Number.isNaN(Date.parse(result2))) throw new Error("Native evidence timestamp is invalid"); + return result2; +} +function acceptanceCriteriaHash(criteria) { + return canonicalHash( + "comet.native.acceptance-set.v1", + [...criteria].sort((left, right) => compareText4(left.id, right.id)) + ); +} +function compareText4(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function buildNativeAcceptanceEvidenceTrace(criteria, evidence, options) { + const nativeRootRef3 = portableRef(options.nativeRootRef, "Native root ref"); + const byId = new Map(criteria.map((criterion2) => [criterion2.id, criterion2])); + if (byId.size !== criteria.length) + throw new Error("Native contract has duplicate acceptance IDs"); + const evidenceById = /* @__PURE__ */ new Map(); + for (const entry2 of evidence) { + if (!byId.has(entry2.acceptance_id)) { + throw new Error(`Verification references unknown acceptance ID: ${entry2.acceptance_id}`); + } + if (evidenceById.has(entry2.acceptance_id)) { + throw new Error(`Verification repeats acceptance ID: ${entry2.acceptance_id}`); + } + evidenceById.set(entry2.acceptance_id, entry2); + } + const missing = [...byId.keys()].filter((id) => !evidenceById.has(id)); + if (missing.length > 0) { + const shown = missing.slice(0, MISSING_ACCEPTANCE_DETAIL_LIMIT); + const remainder = missing.length - shown.length; + throw new Error( + `Verification is missing ${missing.length} acceptance evidence entr${missing.length === 1 ? "y" : "ies"}: ${shown.join(", ")}${remainder > 0 ? `, ... (${remainder} more)` : ""}` + ); + } + const entries = [...byId.values()].sort((left, right) => compareText4(left.id, right.id)).map((criterion2) => { + const entry2 = evidenceById.get(criterion2.id); + const evidenceRefs = [...entry2.evidence_refs].map( + (reference) => portableEvidenceRef(reference, `Evidence ref for ${criterion2.id}`, nativeRootRef3) + ).sort(); + if (new Set(evidenceRefs).size !== evidenceRefs.length) { + throw new Error(`Verification repeats an evidence ref for ${criterion2.id}`); + } + const rawSkippedReason = entry2.skipped_reason?.trim() || null; + const skippedReason = rawSkippedReason === null ? null : requiredText(rawSkippedReason, `Skipped reason for ${criterion2.id}`); + if (evidenceRefs.length === 0 === (skippedReason === null)) { + throw new Error( + `Acceptance ${criterion2.id} requires exactly one of evidence refs or skipped reason` + ); + } + return { + acceptanceId: criterion2.id, + kind: criterion2.kind, + source: portableRef(criterion2.source, `Acceptance source for ${criterion2.id}`), + evidenceRefs, + skippedReason + }; + }); + const criteriaHash = acceptanceCriteriaHash(criteria); + const content = { + schema: "comet.native.acceptance-trace.v1", + nativeRootRef: nativeRootRef3, + criteriaHash, + total: entries.length, + evidenced: entries.filter((entry2) => entry2.evidenceRefs.length > 0).length, + skipped: entries.filter((entry2) => entry2.skippedReason !== null).length, + entries + }; + return { + ...content, + traceHash: canonicalHash(ACCEPTANCE_TRACE_HASH_TAG, content) + }; +} +function buildNativePartialAllowance(input) { + const scope = parseNativeImplementationScopeBundle(input.scopeBundle).scope; + if (scope.complete) throw new Error("Complete implementation scope cannot be partially allowed"); + const unresolved = new Set(scope.unresolvedScopes.map((entry2) => entry2.id)); + if (new Set(input.allowedScopeIds).size !== input.allowedScopeIds.length) { + throw new Error("Partial allowance has duplicate allowed scope IDs"); + } + const scopeIds = [...input.allowedScopeIds].sort(compareText4); + if (scopeIds.length === 0) throw new Error("Partial allowance requires at least one scope ID"); + if ([...unresolved, ...scopeIds].some((id) => !/^scope:[a-f0-9]{64}$/u.test(id))) { + throw new Error("Partial allowance scope IDs are invalid"); + } + const unknown = scopeIds.filter((id) => !unresolved.has(id)); + const missing = [...unresolved].filter((id) => !scopeIds.includes(id)); + if (unknown.length > 0) throw new Error(`Partial allowance has unknown scope IDs: ${unknown}`); + if (missing.length > 0) throw new Error(`Partial allowance is missing scope IDs: ${missing}`); + const content = { + schema: "comet.native.partial-allowance.v1", + change: changeName(input.change), + scopeHash: scope.scopeHash, + scopeIds, + reason: requiredText(input.reason, "Partial allowance reason"), + confirmedSummary: requiredText(input.confirmedSummary, "Partial allowance confirmation"), + sourceRevision: positiveRevision2(input.sourceRevision), + confirmedAt: timestamp(input.now ?? /* @__PURE__ */ new Date()) + }; + return { + ...content, + allowanceHash: canonicalHash(PARTIAL_ALLOWANCE_HASH_TAG, content) + }; +} +function buildNativeVerificationEvidenceEnvelope(input) { + if (input.result !== "pass" && input.result !== "fail") { + throw new Error("Native verification evidence result is invalid"); + } + const acceptanceTrace = parseNativeAcceptanceEvidenceTrace(input.acceptanceTrace); + const implementationScope = parseNativeImplementationScopeBundle( + input.implementationScope.bundle + ).scope; + const implementationScopeRef = evidenceDocumentRef( + input.implementationScope.ref, + "scopes", + implementationScope.scopeHash + ); + if (implementationScope.contractHash !== input.contractHash) { + throw new Error("Implementation scope does not match the verification contract"); + } + const allowanceInput = input.partialAllowance ?? null; + const parsedAllowance = allowanceInput ? parseNativePartialAllowance(allowanceInput.allowance) : null; + const allowance = allowanceInput && parsedAllowance ? { + ref: evidenceDocumentRef(allowanceInput.ref, "allowances", parsedAllowance.allowanceHash), + allowance: parsedAllowance + } : null; + if (implementationScope.complete && allowance !== null) { + throw new Error("Complete implementation scope must not use a partial allowance"); + } + if (!implementationScope.complete && allowance === null) { + throw new Error("Partial implementation scope requires a confirmed allowance"); + } + const unresolvedScopeIds = implementationScope.unresolvedScopes.map((entry2) => entry2.id).sort(compareText4); + if (allowance && (allowance.allowance.change !== input.change || allowance.allowance.scopeHash !== implementationScope.scopeHash || JSON.stringify(allowance.allowance.scopeIds) !== JSON.stringify(unresolvedScopeIds))) { + throw new Error("Partial allowance does not match the verification scope"); + } + if (acceptanceTrace.criteriaHash !== input.acceptanceHash) { + throw new Error("Acceptance trace does not match the verification contract"); + } + if (allowance && allowance.allowance.sourceRevision >= input.sourceRevision) { + throw new Error("Partial allowance must precede the verification evidence revision"); + } + const content = { + schema: "comet.native.verification-evidence.v1", + change: changeName(input.change), + sourceRevision: positiveRevision2(input.sourceRevision), + result: input.result, + freshness: implementationScope.complete ? "complete" : "partial", + contractHash: hash2(input.contractHash, "Verification contractHash"), + acceptanceCriteriaHash: hash2(input.acceptanceHash, "Verification acceptanceHash"), + implementationScopeRef, + implementationScopeHash: implementationScope.scopeHash, + reportRef: portableEvidenceRef(input.reportRef, "Verification report ref"), + reportHash: hash2(input.reportHash, "Verification report hash"), + acceptanceTrace, + partialAllowanceRef: allowance?.ref ?? null, + partialAllowanceHash: allowance?.allowance.allowanceHash ?? null, + receiptRef: input.receiptRef ? checkReceiptRef(input.receiptRef) : null, + createdAt: timestamp(input.now ?? /* @__PURE__ */ new Date()) + }; + return { + ...content, + envelopeHash: canonicalHash(VERIFICATION_ENVELOPE_HASH_TAG, content) + }; +} +function evidenceRecord(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactEvidenceKeys(value, keys, label) { + const expected = new Set(keys); + const unknown = Object.keys(value).filter((key) => !expected.has(key)); + const missing = keys.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function canonicalTimestamp(value, label) { + if (typeof value !== "string") throw new Error(`${label} is invalid`); + const parsed = new Date(value); + if (Number.isNaN(parsed.valueOf()) || parsed.toISOString() !== value) { + throw new Error(`${label} is invalid`); + } + return value; +} +function evidenceDocumentRef(value, kind, hash6) { + if (typeof value !== "string") throw new Error(`Native ${kind} evidence ref is invalid`); + const expected = `runtime/evidence/${kind}/${hash6}.json`; + if (value !== expected) throw new Error(`Native ${kind} evidence ref/hash mismatch`); + return value; +} +function parseNativeAcceptanceEvidenceTrace(value) { + const root = evidenceRecord(value, "Native acceptance trace"); + exactEvidenceKeys( + root, + [ + "schema", + "nativeRootRef", + "criteriaHash", + "total", + "evidenced", + "skipped", + "entries", + "traceHash" + ], + "Native acceptance trace" + ); + if (root.schema !== "comet.native.acceptance-trace.v1" || typeof root.nativeRootRef !== "string" || typeof root.criteriaHash !== "string" || !HASH_PATTERN6.test(root.criteriaHash) || !Number.isSafeInteger(root.total) || !Number.isSafeInteger(root.evidenced) || !Number.isSafeInteger(root.skipped) || !Array.isArray(root.entries)) { + throw new Error("Native acceptance trace is invalid"); + } + const nativeRootRef3 = portableRef(root.nativeRootRef, "Native root ref"); + const entries = root.entries.map((value2, index) => { + const entry2 = evidenceRecord(value2, `Native acceptance trace entry ${index}`); + exactEvidenceKeys( + entry2, + ["acceptanceId", "kind", "source", "evidenceRefs", "skippedReason"], + `Native acceptance trace entry ${index}` + ); + if (typeof entry2.acceptanceId !== "string" || !/^acceptance-[a-f0-9]{64}$/u.test(entry2.acceptanceId) || entry2.kind !== "brief-example" && entry2.kind !== "spec-scenario" || typeof entry2.source !== "string" || !Array.isArray(entry2.evidenceRefs) || entry2.evidenceRefs.some((reference) => typeof reference !== "string") || entry2.skippedReason !== null && (typeof entry2.skippedReason !== "string" || entry2.skippedReason.length === 0 || entry2.skippedReason.trim() !== entry2.skippedReason)) { + throw new Error(`Native acceptance trace entry ${index} is invalid`); + } + const evidenceRefs = entry2.evidenceRefs.map( + (reference) => portableEvidenceRef( + reference, + `Native acceptance trace entry ${index} evidence ref`, + nativeRootRef3 + ) + ); + if (JSON.stringify(evidenceRefs) !== JSON.stringify([...new Set(evidenceRefs)].sort(compareText4)) || evidenceRefs.length === 0 === (entry2.skippedReason === null)) { + throw new Error(`Native acceptance trace entry ${index} evidence state is invalid`); + } + return { + acceptanceId: entry2.acceptanceId, + kind: entry2.kind, + source: portableRef(entry2.source, `Native acceptance trace entry ${index} source`), + evidenceRefs, + skippedReason: entry2.skippedReason === null ? null : requiredText( + entry2.skippedReason, + `Native acceptance trace entry ${index} skipped reason` + ) + }; + }); + if (JSON.stringify(entries) !== JSON.stringify( + [...entries].sort((left, right) => compareText4(left.acceptanceId, right.acceptanceId)) + ) || new Set(entries.map((entry2) => entry2.acceptanceId)).size !== entries.length || root.total !== entries.length || root.evidenced !== entries.filter((entry2) => entry2.evidenceRefs.length > 0).length || root.skipped !== entries.filter((entry2) => entry2.skippedReason !== null).length) { + throw new Error("Native acceptance trace entries are inconsistent"); + } + const content = { + schema: "comet.native.acceptance-trace.v1", + nativeRootRef: nativeRootRef3, + criteriaHash: root.criteriaHash, + total: root.total, + evidenced: root.evidenced, + skipped: root.skipped, + entries + }; + const traceHash = hash2(root.traceHash, "Native acceptance trace hash"); + if (canonicalHash(ACCEPTANCE_TRACE_HASH_TAG, content) !== traceHash) { + throw new Error("Native acceptance trace content hash mismatch"); + } + return { ...content, traceHash }; +} +function parseNativePartialAllowance(value) { + const root = evidenceRecord(value, "Native partial allowance"); + exactEvidenceKeys( + root, + [ + "schema", + "change", + "scopeHash", + "scopeIds", + "reason", + "confirmedSummary", + "sourceRevision", + "confirmedAt", + "allowanceHash" + ], + "Native partial allowance" + ); + if (root.schema !== "comet.native.partial-allowance.v1" || typeof root.change !== "string" || typeof root.scopeHash !== "string" || !Array.isArray(root.scopeIds) || root.scopeIds.length === 0 || root.scopeIds.some((id) => typeof id !== "string" || !/^scope:[a-f0-9]{64}$/u.test(id)) || JSON.stringify(root.scopeIds) !== JSON.stringify([...new Set(root.scopeIds)].sort(compareText4)) || typeof root.reason !== "string" || typeof root.confirmedSummary !== "string") { + throw new Error("Native partial allowance is invalid"); + } + const content = { + schema: "comet.native.partial-allowance.v1", + change: changeName(root.change), + scopeHash: hash2(root.scopeHash, "Native partial allowance scopeHash"), + scopeIds: root.scopeIds, + reason: requiredText(root.reason, "Partial allowance reason"), + confirmedSummary: requiredText(root.confirmedSummary, "Partial allowance confirmation"), + sourceRevision: positiveRevision2(root.sourceRevision), + confirmedAt: canonicalTimestamp(root.confirmedAt, "Native partial allowance timestamp") + }; + if (content.reason !== root.reason || content.confirmedSummary !== root.confirmedSummary) { + throw new Error("Native partial allowance text is not canonical"); + } + const allowanceHash = hash2(root.allowanceHash, "Native partial allowance hash"); + if (canonicalHash(PARTIAL_ALLOWANCE_HASH_TAG, content) !== allowanceHash) { + throw new Error("Native partial allowance content hash mismatch"); + } + return { ...content, allowanceHash }; +} +function parseNativeVerificationEvidenceEnvelope(value) { + const root = evidenceRecord(value, "Native verification evidence"); + exactEvidenceKeys( + root, + [ + "schema", + "change", + "sourceRevision", + "result", + "freshness", + "contractHash", + "acceptanceCriteriaHash", + "implementationScopeRef", + "implementationScopeHash", + "reportRef", + "reportHash", + "acceptanceTrace", + "partialAllowanceRef", + "partialAllowanceHash", + "receiptRef", + "createdAt", + "envelopeHash" + ], + "Native verification evidence" + ); + if (root.schema !== "comet.native.verification-evidence.v1" || typeof root.change !== "string" || root.result !== "pass" && root.result !== "fail" || root.freshness !== "complete" && root.freshness !== "partial" || typeof root.implementationScopeHash !== "string" || typeof root.reportRef !== "string" || root.receiptRef !== null && typeof root.receiptRef !== "string") { + throw new Error("Native verification evidence is invalid"); + } + const implementationScopeHash = hash2( + root.implementationScopeHash, + "Native verification implementation scope hash" + ); + const acceptanceTrace = parseNativeAcceptanceEvidenceTrace(root.acceptanceTrace); + const acceptanceCriteriaHash2 = hash2( + root.acceptanceCriteriaHash, + "Native verification acceptance criteria hash" + ); + if (acceptanceTrace.criteriaHash !== acceptanceCriteriaHash2) { + throw new Error("Native verification acceptance trace does not match its criteria hash"); + } + const hasAllowance = root.partialAllowanceRef !== null || root.partialAllowanceHash !== null; + if (root.partialAllowanceRef === null !== (root.partialAllowanceHash === null) || root.freshness === "complete" && hasAllowance || root.freshness === "partial" && !hasAllowance) { + throw new Error("Native verification partial allowance state is invalid"); + } + const partialAllowanceHash = root.partialAllowanceHash === null ? null : hash2(root.partialAllowanceHash, "Native verification allowance hash"); + const result2 = root.result; + const freshness = root.freshness; + const content = { + schema: "comet.native.verification-evidence.v1", + change: changeName(root.change), + sourceRevision: positiveRevision2(root.sourceRevision), + result: result2, + freshness, + contractHash: hash2(root.contractHash, "Native verification contract hash"), + acceptanceCriteriaHash: acceptanceCriteriaHash2, + implementationScopeRef: evidenceDocumentRef( + root.implementationScopeRef, + "scopes", + implementationScopeHash + ), + implementationScopeHash, + reportRef: portableEvidenceRef(root.reportRef, "Native verification report ref"), + reportHash: hash2(root.reportHash, "Native verification report hash"), + acceptanceTrace, + partialAllowanceRef: partialAllowanceHash === null ? null : evidenceDocumentRef(root.partialAllowanceRef, "allowances", partialAllowanceHash), + partialAllowanceHash, + receiptRef: root.receiptRef === null ? null : checkReceiptRef(root.receiptRef), + createdAt: canonicalTimestamp(root.createdAt, "Native verification timestamp") + }; + const envelopeHash = hash2(root.envelopeHash, "Native verification envelope hash"); + if (canonicalHash(VERIFICATION_ENVELOPE_HASH_TAG, content) !== envelopeHash) { + throw new Error("Native verification evidence content hash mismatch"); + } + return { ...content, envelopeHash }; +} + +// domains/comet-native/native-evidence-storage.ts +var HASH_PATTERN7 = /^[a-f0-9]{64}$/u; +var MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES = MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES; +var MAX_NATIVE_IMPLEMENTATION_SCOPE_BUNDLE_BYTES = 3 * MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES; +function isInside5(parent, target) { + const relative = path22.relative(parent, target); + return relative === "" || !path22.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path22.sep}`); +} +function sameDirectoryIdentity5(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity6(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryIdentity3(directory) { + const stat = await fs15.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native evidence parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs15.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain4(root, directory) { + const lexicalRoot = path22.resolve(root); + const lexicalDirectory = path22.resolve(directory); + if (!isInside5(lexicalRoot, lexicalDirectory)) { + throw new Error("Native evidence path is outside its change"); + } + const chain = [await captureDirectoryIdentity3(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path22.relative(lexicalRoot, lexicalDirectory).split(path22.sep).filter(Boolean)) { + cursor = path22.join(cursor, segment); + const identity = await captureDirectoryIdentity3(cursor); + if (!isInside5(chain[0].realPath, identity.realPath)) { + throw new Error(`Native evidence parent resolves outside its change: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain5(chain) { + for (const identity of chain) { + const stat = await fs15.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity5(identity, stat) || await fs15.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native evidence parent changed while reading: ${identity.path}`); + } + } +} +async function readBoundedEvidenceJson(file, changeRoot, hooks = {}) { + const chain = await captureDirectoryChain4(changeRoot, path22.dirname(file)); + await hooks.afterParentChainCaptured?.(); + const lexical = await fs15.lstat(file); + if (!lexical.isFile() || lexical.isSymbolicLink()) { + throw new Error("Native evidence document must be a regular file"); + } + const realPath = await fs15.realpath(file); + if (!isInside5(chain[0].realPath, realPath)) { + throw new Error("Native evidence document resolves outside its change"); + } + const handle = await fs15.open(file, "r"); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs15.lstat(file), + fs15.realpath(file) + ]); + await verifyDirectoryChain5(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== realPath || !sameFileIdentity6(opened, lexical) || !sameFileIdentity6(opened, pathAfterOpen)) { + throw new Error("Native evidence document changed while opening"); + } + if (opened.size > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error( + `Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes` + ); + } + await hooks.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(64 * 1024); + while (true) { + const remaining = MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error( + `Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes` + ); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + await hooks.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs15.lstat(file), + fs15.realpath(file) + ]); + await verifyDirectoryChain5(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity6(opened, afterHandle) || !sameFileIdentity6(opened, afterPath)) { + throw new Error("Native evidence document changed while reading"); + } + return JSON.parse(Buffer.concat(chunks, total).toString("utf8")); + } finally { + await handle.close(); + } +} +function nativeEvidenceRef(kind, hash6) { + if (!HASH_PATTERN7.test(hash6)) throw new Error("Native evidence hash is invalid"); + return `runtime/evidence/${kind}/${hash6}.json`; +} +function nativeReportEvidenceRef(hash6) { + return nativeEvidenceRef("reports", hash6); +} +async function writeNativeVerificationReportSnapshot(options) { + const encoded = Buffer.from(options.text, "utf8"); + if (encoded.byteLength > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES || createHash12("sha256").update(encoded).digest("hex") !== options.hash) { + throw new Error("Native verification report snapshot hash or size is invalid"); + } + return writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "reports", + hash: options.hash, + value: { + schema: "comet.native.verification-report.v1", + reportHash: options.hash, + content: options.text + } + }); +} +async function readNativeVerificationReportSnapshot(paths, name, hash6) { + if (!HASH_PATTERN7.test(hash6)) throw new Error("Native report evidence hash is invalid"); + const value = await readEvidenceDocument({ paths, name, kind: "reports", hash: hash6 }); + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native report evidence must be an object"); + } + const report = value; + if (Object.keys(report).sort().join(",") !== "content,reportHash,schema" || report.schema !== "comet.native.verification-report.v1" || report.reportHash !== hash6 || typeof report.content !== "string" || createHash12("sha256").update(Buffer.from(report.content, "utf8")).digest("hex") !== hash6) { + throw new Error("Native report evidence does not match its hash"); + } + return report.content; +} +function parseEvidenceRef(ref, expectedKind) { + const match = /^runtime\/evidence\/(snapshots|scopes|allowances|verifications|reports)\/([a-f0-9]{64})\.json$/u.exec( + ref + ); + if (!match || match[1] !== expectedKind) { + throw new Error(`Native evidence ref is invalid for ${expectedKind}`); + } + return match[2]; +} +function evidenceFile(paths, name, kind, hash6) { + return path22.join(nativeChangeDir(paths, name), ...nativeEvidenceRef(kind, hash6).split("/")); +} +async function readEvidenceDocument(options) { + const file = evidenceFile(options.paths, options.name, options.kind, options.hash); + await resolveContainedNativePath(options.paths.nativeRoot, file); + return readBoundedEvidenceJson(file, nativeChangeDir(options.paths, options.name), options.hooks); +} +async function writeEvidenceDocument(options) { + assertEvidenceDocumentBudget(options.value); + const file = evidenceFile(options.paths, options.name, options.kind, options.hash); + await resolveContainedNativePath(options.paths.nativeRoot, file); + try { + const existing = await readEvidenceDocument(options); + if (JSON.stringify(existing) !== JSON.stringify(options.value)) { + throw new Error(`Native evidence hash collision for ${options.hash}`); + } + return nativeEvidenceRef(options.kind, options.hash); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, options.value, { containedRoot: options.paths.nativeRoot }); + const persisted = await readEvidenceDocument(options); + if (JSON.stringify(persisted) !== JSON.stringify(options.value)) { + throw new Error(`Native evidence changed during commit for ${options.hash}`); + } + return nativeEvidenceRef(options.kind, options.hash); +} +function assertEvidenceDocumentBudget(value) { + if (serializedEvidenceBytes2(value) > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error(`Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes`); + } +} +function serializedEvidenceBytes2(value) { + return Buffer.byteLength(JSON.stringify(value, null, 2) + "\n", "utf8"); +} +function parseSnapshot2(value, expectedHash) { + return parseNativeSnapshotProjection(value, expectedHash); +} +function parseScope(value, expectedHash) { + const scope = parseNativeImplementationScope(value); + if (scope.scopeHash !== expectedHash) { + throw new Error("Native implementation scope ref/hash mismatch"); + } + return scope; +} +function parseAllowance(value, expectedName, expectedHash) { + const allowance = parseNativePartialAllowance(value); + if (allowance.change !== expectedName || allowance.allowanceHash !== expectedHash) { + throw new Error("Native partial allowance ref/hash/change mismatch"); + } + return allowance; +} +function parseEnvelope(value, expectedName, expectedHash) { + const evidence = parseNativeVerificationEvidenceEnvelope(value); + if (evidence.change !== expectedName || evidence.envelopeHash !== expectedHash) { + throw new Error("Native verification evidence ref/hash/change mismatch"); + } + return evidence; +} +async function assertEnvelopeDependencies(paths, name, evidence, requireReportSnapshot = false) { + const scope = await readNativeImplementationScope(paths, name, evidence.implementationScopeRef); + if (requireReportSnapshot) { + await readNativeVerificationReportSnapshot(paths, name, evidence.reportHash); + } + if (scope.scopeHash !== evidence.implementationScopeHash || scope.contractHash !== evidence.contractHash || (scope.complete ? "complete" : "partial") !== evidence.freshness) { + throw new Error("Native verification evidence does not match its implementation scope"); + } + if (evidence.partialAllowanceRef === null) return; + const allowance = await readNativePartialAllowance(paths, name, evidence.partialAllowanceRef); + if (allowance.allowanceHash !== evidence.partialAllowanceHash || allowance.scopeHash !== scope.scopeHash || JSON.stringify(allowance.scopeIds) !== JSON.stringify(scope.unresolvedScopes.map((entry2) => entry2.id).sort()) || allowance.sourceRevision >= evidence.sourceRevision) { + throw new Error("Native verification evidence does not match its partial allowance"); + } +} +async function writeNativeImplementationScope(options) { + const bundle = parseNativeImplementationScopeBundle(options.bundle); + const { baseline, current, scope } = bundle; + assertEvidenceDocumentBudget(baseline); + assertEvidenceDocumentBudget(current); + assertEvidenceDocumentBudget(scope); + await writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "snapshots", + hash: scope.baselineProjectionHash, + value: baseline + }); + await writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "snapshots", + hash: scope.currentProjectionHash, + value: current + }); + return writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "scopes", + hash: scope.scopeHash, + value: scope + }); +} +async function readNativeImplementationScopeBundle(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "scopes"); + const scope = parseScope( + await readEvidenceDocument({ paths, name, kind: "scopes", hash: hash6, hooks }), + hash6 + ); + const baselineHash = parseEvidenceRef(scope.baselineProjectionRef, "snapshots"); + const currentHash = parseEvidenceRef(scope.currentProjectionRef, "snapshots"); + const [baseline, current] = await Promise.all([ + readEvidenceDocument({ paths, name, kind: "snapshots", hash: baselineHash }).then( + (value) => parseSnapshot2(value, baselineHash) + ), + readEvidenceDocument({ paths, name, kind: "snapshots", hash: currentHash }).then( + (value) => parseSnapshot2(value, currentHash) + ) + ]); + return rebuildNativeImplementationScopeBundle({ baseline, current, scope }); +} +async function readNativeImplementationScope(paths, name, ref, hooks) { + return (await readNativeImplementationScopeBundle(paths, name, ref, hooks)).scope; +} +async function writeNativePartialAllowance(options) { + const allowance = parseAllowance( + options.allowance, + options.name, + options.allowance.allowanceHash + ); + const scope = await readNativeImplementationScope( + options.paths, + options.name, + nativeEvidenceRef("scopes", allowance.scopeHash) + ); + const unresolvedScopeIds = scope.unresolvedScopes.map((entry2) => entry2.id).sort(); + if (scope.complete || JSON.stringify(unresolvedScopeIds) !== JSON.stringify(allowance.scopeIds)) { + throw new Error("Native partial allowance does not match a persisted partial scope"); + } + return writeEvidenceDocument({ + ...options, + kind: "allowances", + hash: allowance.allowanceHash, + value: allowance + }); +} +async function readNativePartialAllowance(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "allowances"); + return parseAllowance( + await readEvidenceDocument({ paths, name, kind: "allowances", hash: hash6, hooks }), + name, + hash6 + ); +} +async function writeNativeVerificationEvidence(options) { + const evidence = parseEnvelope(options.evidence, options.name, options.evidence.envelopeHash); + await assertEnvelopeDependencies(options.paths, options.name, evidence, true); + return writeEvidenceDocument({ + ...options, + kind: "verifications", + hash: evidence.envelopeHash, + value: evidence + }); +} +async function readNativeVerificationEvidence(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "verifications"); + const evidence = parseEnvelope( + await readEvidenceDocument({ paths, name, kind: "verifications", hash: hash6, hooks }), + name, + hash6 + ); + await assertEnvelopeDependencies(paths, name, evidence); + return evidence; +} + +// domains/comet-native/native-conflict-inspection.ts +async function visibleChangeEntries(paths) { + try { + const directory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: paths.changesDir, + label: "Native conflict changes directory", + maxEntries: NATIVE_CONFLICT_RADAR_LIMITS.maxChanges + }); + await directory.verify(); + return directory.entries; + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } +} +async function collectConflictInput(paths, name) { + const state = await readNativeChange(paths, name); + const [scope, workspace] = await Promise.all([ + state.implementation_scope ? readNativeImplementationScope(paths, name, state.implementation_scope) : null, + // Workspace identity is advisory. A malformed advisory must not suppress deterministic + // capability/artifact conflict facts. + readNativeWorkspaceIdentity(paths, name).catch(() => null) + ]); + return { + name: state.name, + revision: state.revision, + specs: state.spec_changes.map((spec) => ({ + capability: spec.capability, + operation: spec.operation, + baseHash: spec.base_hash + })), + declaredArtifacts: scope?.declaredArtifacts ?? [], + workspaceIdentityHash: workspace?.nativeRootId ?? null + }; +} +async function collectConflictInputs(paths) { + const entries = await visibleChangeEntries(paths); + const names = entries.filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + return Promise.all(names.map((name) => collectConflictInput(paths, name))); +} +async function inspectNativeChangeConflicts(paths, name) { + const input = await collectConflictInputs(paths); + buildNativeConflictRadar(input); + const target = input.find((change) => change.name === name); + if (!target) throw new Error(`Native conflict target is not visible: ${name}`); + let definiteConflictCount = 0; + let possibleOverlapCount = 0; + for (const other of input) { + if (other.name === name) continue; + const relationship2 = buildNativeConflictRadar([target, other]).relationships[0]; + if (relationship2.classification === "definite-conflict") definiteConflictCount += 1; + if (relationship2.classification === "possible-overlap") possibleOverlapCount += 1; + } + return { + definiteConflictCount, + possibleOverlapCount, + findingCodes: [ + ...definiteConflictCount > 0 ? ["native-change-conflict"] : [], + ...possibleOverlapCount > 0 ? ["native-change-overlap"] : [] + ] + }; +} + +// domains/comet-native/native-transition-journal.ts +import { randomUUID as randomUUID3 } from "crypto"; +import { promises as fs16 } from "fs"; +import path23 from "path"; +import { isDeepStrictEqual } from "util"; + +// domains/comet-native/native-repair-stagnation.ts +var NATIVE_REPAIR_SIGNATURE_SCHEMA = "comet.native.repair-signature.v1"; +var NATIVE_REPAIR_STAGNATION_LIMITS = { + warningAtConsecutiveFailures: 2, + manualStopAtConsecutiveFailures: 3, + maxRepairIterations: 12, + maxHistoryRecords: 64, + maxCategories: 16, + maxFailedCheckIds: 128, + maxOverrideSummaryCharacters: 2e3 +}; +var SIGNATURE_HASH_TAG = "comet.native.repair-signature.v1"; +var OVERRIDE_SUMMARY_HASH_TAG = "comet.native.repair-override-summary.v1"; +var HASH_PATTERN8 = /^[a-f0-9]{64}$/u; +var TOKEN_PATTERN = /^[a-z0-9][a-z0-9._:/-]{0,255}$/u; +function compareText5(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function hash3(value, label) { + if (typeof value !== "string" || !HASH_PATTERN8.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function positiveInteger3(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function normalizedTokens(values, label, max, allowEmpty) { + if (!Array.isArray(values) || values.length > max || !allowEmpty && values.length === 0) { + throw new Error(`${label} exceeds its count boundary`); + } + const tokens = values.map((value) => { + if (typeof value !== "string" || !TOKEN_PATTERN.test(value)) { + throw new Error(`${label} contains an invalid token: ${String(value)}`); + } + return value; + }); + return [...new Set(tokens)].sort(compareText5); +} +function normalizeNativeRepairFailureTokens(options) { + const categories = options.categories && options.categories.length > 0 ? options.categories : ["verification-failed"]; + return { + categories: normalizedTokens( + categories, + "Native repair categories", + NATIVE_REPAIR_STAGNATION_LIMITS.maxCategories, + false + ), + failedCheckIds: normalizedTokens( + options.failedCheckIds ?? [], + "Native repair failed check IDs", + NATIVE_REPAIR_STAGNATION_LIMITS.maxFailedCheckIds, + true + ) + }; +} +function buildNativeRepairSignature(facts) { + const tokens = normalizeNativeRepairFailureTokens(facts); + const content = { + schema: NATIVE_REPAIR_SIGNATURE_SCHEMA, + contractHash: hash3(facts.contractHash, "Native repair contract hash"), + implementationScopeHash: hash3( + facts.implementationScopeHash, + "Native repair implementation scope hash" + ), + artifactSnapshotHash: hash3(facts.artifactSnapshotHash, "Native repair artifact snapshot hash"), + categories: tokens.categories, + failedCheckIds: tokens.failedCheckIds + }; + return { + ...content, + signatureHash: canonicalHash(SIGNATURE_HASH_TAG, content) + }; +} +function normalizeHistory(history) { + if (!Array.isArray(history) || history.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxHistoryRecords) { + throw new Error("Native repair history exceeds its record boundary"); + } + let previousIteration = 0; + let previousRevision = 0; + return history.map((record8, index) => { + if (!record8 || record8.kind !== "failure" && record8.kind !== "override") { + throw new Error(`Native repair history record ${index} is invalid`); + } + const expectedKeys = record8.kind === "failure" ? ["iteration", "kind", "revision", "signatureHash"] : ["iteration", "kind", "revision", "signatureHash", "summaryHash"]; + const keys = Object.keys(record8).sort(compareText5); + if (keys.length !== expectedKeys.length || keys.some((key, keyIndex) => key !== expectedKeys[keyIndex])) { + throw new Error(`Native repair history record ${index} fields are invalid`); + } + const iteration = positiveInteger3(record8.iteration, `Native repair history ${index} iteration`); + const revision = positiveInteger3(record8.revision, `Native repair history ${index} revision`); + const previous = index > 0 ? history[index - 1] : null; + const pairedOverride = record8.kind === "override" && previous?.kind === "failure" && iteration === previousIteration && revision >= previousRevision && record8.signatureHash === previous.signatureHash; + if (!pairedOverride && (iteration <= previousIteration || revision <= previousRevision)) { + throw new Error("Native repair history must be strictly ordered by revision and iteration"); + } + previousIteration = iteration; + previousRevision = revision; + const signatureHash = hash3(record8.signatureHash, `Native repair history ${index} signature`); + if (record8.kind === "failure") return { kind: "failure", iteration, revision, signatureHash }; + return { + kind: "override", + iteration, + revision, + signatureHash, + summaryHash: hash3(record8.summaryHash, `Native repair history ${index} summary`) + }; + }); +} +function overrideSummary(value) { + const summary = value.trim(); + if (summary.length === 0 || summary.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxOverrideSummaryCharacters) { + throw new Error("Native repair override summary is invalid"); + } + return summary; +} +function hashNativeRepairOverrideSummary(summary) { + return canonicalHash(OVERRIDE_SUMMARY_HASH_TAG, { + summary: overrideSummary(summary) + }); +} +function decideNativeRepairStagnation(options) { + const signature = buildNativeRepairSignature(options.facts); + const history = normalizeHistory(options.history); + const failures = history.filter( + (record8) => record8.kind === "failure" + ); + const totalRepairFailures = failures.length + 1; + const remainingIterations = Math.max( + 0, + NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations - totalRepairFailures + ); + let consecutiveFailures = 1; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== signature.signatureHash) break; + consecutiveFailures += 1; + } + if (totalRepairFailures >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + return { + disposition: "hard-stop", + reasonCode: "repair-iteration-limit", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (consecutiveFailures < NATIVE_REPAIR_STAGNATION_LIMITS.warningAtConsecutiveFailures) { + return { + disposition: "continue", + reasonCode: "new-failure-signature", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (consecutiveFailures < NATIVE_REPAIR_STAGNATION_LIMITS.manualStopAtConsecutiveFailures) { + return { + disposition: "warn", + reasonCode: "repeated-failure-warning", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (options.override) { + const expected = hash3( + options.override.expectedSignatureHash, + "Native repair override expected signature" + ); + if (expected !== signature.signatureHash) { + throw new Error("Native repair override does not match the current failure signature"); + } + overrideSummary(options.override.summary); + const alreadyUsed2 = history.some( + (record8) => record8.kind === "override" && record8.signatureHash === signature.signatureHash + ); + return { + disposition: alreadyUsed2 ? "manual-stop" : "continue", + reasonCode: alreadyUsed2 ? "override-already-used" : "override-accepted", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: !alreadyUsed2 + }; + } + const alreadyUsed = history.some( + (record8) => record8.kind === "override" && record8.signatureHash === signature.signatureHash + ); + return { + disposition: "manual-stop", + reasonCode: alreadyUsed ? "override-already-used" : "repeated-failure-stop", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; +} + +// domains/comet-native/native-trajectory-limits.ts +var NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS = 4096; +function assertNativeTrajectoryText(value, label) { + if (typeof value !== "string" || value.trim().length === 0 || value.length > NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS) { + throw new Error( + `${label} must be between 1 and ${NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS} characters` + ); + } +} + +// domains/comet-native/native-repair-runtime.ts +var NATIVE_REPAIR_TRAJECTORY_FIELD = "repairStagnation"; +var NATIVE_REPAIR_TRAJECTORY_LIMITS = { + maxEvents: 4096, + maxDataDepth: 8, + maxDataNodes: 4096, + maxTotalDataNodes: 65536, + maxObjectFields: 64, + maxArrayEntries: 256, + maxKeyCharacters: 128, + maxTextCharacters: NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS, + maxEventDataCharacters: 65536, + maxTotalDataCharacters: 1048576, + maxRunIdCharacters: 256 +}; +var HASH_PATTERN9 = /^[a-f0-9]{64}$/u; +var REPAIR_SCOPE_HASH_TAG = "comet.native.repair-scope.v1"; +var EVENT_TYPES2 = /* @__PURE__ */ new Set([ + "run_started", + "action_proposed", + "action_completed", + "eval_completed", + "checkpoint", + "state_migrated", + "state_transitioned", + "command_check_recorded", + "recovery_reconciled" +]); +var EVENT_KEYS2 = ["data", "runId", "sequence", "timestamp", "type"]; +var PROJECTION_KEYS = ["disposition", "overrideSummaryHash", "signatureHash"]; +function compareText6(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function exactKeys(value, expected, label) { + const actual = Object.keys(value).sort(compareText6); + if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) { + throw new Error(`${label} fields are invalid`); + } +} +function record5(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + const prototype = Object.getPrototypeOf(value); + if (prototype !== Object.prototype && prototype !== null) { + throw new Error(`${label} must be a plain object`); + } + return value; +} +function hash4(value, label) { + if (typeof value !== "string" || !HASH_PATTERN9.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function nativeRepairScopeHash(bundle) { + const scope = parseNativeImplementationScopeBundle(bundle).scope; + return canonicalHash(REPAIR_SCOPE_HASH_TAG, { + schema: REPAIR_SCOPE_HASH_TAG, + contractHash: scope.contractHash, + artifactSnapshotHash: scope.currentProjectionHash + }); +} +function boundedRunId(value) { + if (typeof value !== "string" || value.length === 0 || value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxRunIdCharacters || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + })) { + throw new Error("Native repair trajectory run ID is invalid"); + } + return value; +} +function boundedData(value, depth, budget, label, legacyTransitionText = false) { + budget.nodes += 1; + if (budget.nodes > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxDataNodes) { + throw new Error("Native repair trajectory event data exceeds its node boundary"); + } + if (depth > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxDataDepth) { + throw new Error("Native repair trajectory event data exceeds its depth boundary"); + } + if (value === null || typeof value === "boolean") return; + if (typeof value === "number") { + if (!Number.isFinite(value)) { + throw new Error(`${label} contains a non-finite number`); + } + return; + } + if (typeof value === "string") { + if (value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTextCharacters && (!legacyTransitionText || value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters)) { + throw new Error(`${label} contains oversized text`); + } + budget.characters += value.length; + if (budget.characters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters) { + throw new Error("Native repair trajectory event data exceeds its text boundary"); + } + return; + } + if (typeof value !== "object") { + throw new Error(`${label} contains a non-JSON value`); + } + if (budget.ancestors.has(value)) throw new Error(`${label} contains a cycle`); + budget.ancestors.add(value); + try { + if (Array.isArray(value)) { + if (value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxArrayEntries) { + throw new Error(`${label} contains an oversized array`); + } + value.forEach((entry2, index) => boundedData(entry2, depth + 1, budget, `${label}[${index}]`)); + return; + } + const object = record5(value, label); + const keys = Object.keys(object); + if (keys.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxObjectFields) { + throw new Error(`${label} contains too many fields`); + } + for (const key of keys) { + if (key.length === 0 || key.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxKeyCharacters) { + throw new Error(`${label} contains an invalid field name`); + } + budget.characters += key.length; + if (budget.characters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters) { + throw new Error("Native repair trajectory event data exceeds its text boundary"); + } + boundedData( + object[key], + depth + 1, + budget, + `${label}.${key}`, + depth === 0 && (key === "summary" || key === "noCodeReason") + ); + } + } finally { + budget.ancestors.delete(value); + } +} +function parseTimestamp(value) { + if (typeof value !== "string" || value.length > 64) { + throw new Error("Native repair trajectory timestamp is invalid"); + } + const parsed = new Date(value); + if (!Number.isFinite(parsed.valueOf()) || parsed.toISOString() !== value) { + throw new Error("Native repair trajectory timestamp is invalid"); + } + return value; +} +function parseEvent2(value, index, runId) { + const event = record5(value, `Native repair trajectory event ${index + 1}`); + exactKeys(event, EVENT_KEYS2, `Native repair trajectory event ${index + 1}`); + if (!Number.isSafeInteger(event.sequence) || event.sequence !== index + 1) { + throw new Error("Native repair trajectory sequence is invalid"); + } + parseTimestamp(event.timestamp); + if (typeof event.type !== "string" || !EVENT_TYPES2.has(event.type)) { + throw new Error("Native repair trajectory event type is invalid"); + } + if (boundedRunId(event.runId) !== runId) { + throw new Error("Native repair trajectory run ID changed inside the committed prefix"); + } + const budget = { nodes: 0, characters: 0, ancestors: /* @__PURE__ */ new Set() }; + boundedData(event.data, 0, budget, "Native trajectory event data"); + record5(event.data, "Native repair trajectory event data"); + return { + event, + dataNodes: budget.nodes, + dataCharacters: budget.characters + }; +} +function parseNativeRepairTrajectoryProjection(value) { + const projection = record5(value, "Native repair trajectory projection"); + exactKeys(projection, PROJECTION_KEYS, "Native repair trajectory projection"); + const signatureHash = hash4(projection.signatureHash, "Native repair trajectory signature hash"); + if (projection.disposition !== "continue" && projection.disposition !== "warn" && projection.disposition !== "manual-stop" && projection.disposition !== "hard-stop") { + throw new Error("Native repair trajectory disposition is invalid"); + } + const overrideSummaryHash = projection.overrideSummaryHash === null ? null : hash4(projection.overrideSummaryHash, "Native repair trajectory override summary hash"); + if (overrideSummaryHash !== null && projection.disposition !== "continue") { + throw new Error("Native repair trajectory override must continue exactly once"); + } + return { + signatureHash, + disposition: projection.disposition, + overrideSummaryHash + }; +} +function assertProjectionTransition(data, projection) { + if (projection.overrideSummaryHash === null) { + if (data.previousPhase !== "verify" || data.nextPhase !== "build" || data.verificationResult !== "fail") { + throw new Error( + "Native repair failure projection is only valid on a failed Verify-to-Build transition" + ); + } + return; + } + if (projection.disposition !== "continue" || data.previousPhase !== "build" || data.nextPhase !== "verify" || data.verificationResult !== null) { + throw new Error( + "Native repair override projection is only valid on a Build-to-Verify transition" + ); + } +} +function assertCommittedFailureProjection(projection, history) { + const failures = history.filter((entry2) => entry2.kind === "failure"); + const total = failures.length + 1; + if (total > NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + throw new Error("Native repair trajectory commits a failure beyond the hard-stop boundary"); + } + let consecutive = 1; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== projection.signatureHash) break; + consecutive += 1; + } + const expectedDisposition = total >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations ? "hard-stop" : consecutive < NATIVE_REPAIR_STAGNATION_LIMITS.warningAtConsecutiveFailures ? "continue" : consecutive < NATIVE_REPAIR_STAGNATION_LIMITS.manualStopAtConsecutiveFailures ? "warn" : "manual-stop"; + if (projection.disposition !== expectedDisposition || projection.overrideSummaryHash !== null) { + throw new Error( + `Native repair trajectory failure disposition is invalid: expected ${expectedDisposition}` + ); + } +} +function assertCommittedOverrideProjection(projection, history, latestProjection) { + const failures = history.filter((entry2) => entry2.kind === "failure"); + if (failures.length >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + throw new Error("Native repair trajectory cannot override a hard stop"); + } + if (latestProjection?.disposition !== "manual-stop" || latestProjection.signatureHash !== projection.signatureHash || failures.at(-1)?.signatureHash !== projection.signatureHash) { + throw new Error("Native repair trajectory override does not match the latest manual stop"); + } + if (history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === projection.signatureHash + )) { + throw new Error("Native repair trajectory signature was already overridden"); + } +} +function projectNativeRepairHistory(options) { + if (!Array.isArray(options.trajectory) || options.trajectory.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEvents) { + throw new Error("Native repair trajectory exceeds its event boundary"); + } + const runId = boundedRunId(options.runId); + if (!Number.isSafeInteger(options.committedTrajectoryOffset) || options.committedTrajectoryOffset < 0 || options.committedTrajectoryOffset > options.trajectory.length) { + throw new Error("Native repair committed trajectory offset is invalid"); + } + const history = []; + let latestProjection = null; + let activeScopeHash = null; + let iteration = 0; + let totalDataNodes = 0; + let totalDataCharacters = 0; + for (let index = 0; index < options.committedTrajectoryOffset; index += 1) { + const parsed = parseEvent2(options.trajectory[index], index, runId); + const { event } = parsed; + totalDataNodes += parsed.dataNodes; + totalDataCharacters += parsed.dataCharacters; + if (totalDataNodes > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTotalDataNodes || totalDataCharacters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTotalDataCharacters) { + throw new Error("Native repair committed trajectory exceeds its aggregate data boundary"); + } + const data = event.data; + const eventScopeHash = Object.hasOwn(data, "repairScopeHash") ? hash4(data.repairScopeHash, "Native repair trajectory repair scope hash") : Object.hasOwn(data, "implementationScopeHash") ? hash4(data.implementationScopeHash, "Native repair trajectory implementation scope hash") : null; + if (!Object.hasOwn(data, NATIVE_REPAIR_TRAJECTORY_FIELD)) { + if (event.type === "state_transitioned" && data.previousPhase === "build" && data.nextPhase === "verify" && (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash || eventScopeHash === null && (latestProjection?.disposition === "manual-stop" || latestProjection?.disposition === "hard-stop"))) { + history.length = 0; + latestProjection = null; + activeScopeHash = eventScopeHash; + iteration = 0; + continue; + } + if (event.type === "state_transitioned" && data.previousPhase === "verify" && data.nextPhase === "archive" && data.verificationResult === "pass") { + history.length = 0; + latestProjection = null; + activeScopeHash = null; + iteration = 0; + } + continue; + } + if (event.type !== "state_transitioned") { + throw new Error("Native repair projection must belong to a state_transitioned event"); + } + const projection = parseNativeRepairTrajectoryProjection(data[NATIVE_REPAIR_TRAJECTORY_FIELD]); + assertProjectionTransition(data, projection); + if (projection.overrideSummaryHash === null) { + if (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash) { + history.length = 0; + iteration = 0; + } + if (eventScopeHash !== null) activeScopeHash = eventScopeHash; + assertCommittedFailureProjection(projection, history); + iteration += 1; + history.push({ + kind: "failure", + revision: event.sequence, + iteration, + signatureHash: projection.signatureHash + }); + } else { + if (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash) { + throw new Error("Native repair override cannot cross implementation scope progress"); + } + assertCommittedOverrideProjection(projection, history, latestProjection); + const failure = history.at(-1); + if (!failure || failure.kind !== "failure") { + throw new Error("Native repair trajectory override has no failure history"); + } + history.push({ + kind: "override", + revision: event.sequence, + iteration: failure.iteration, + signatureHash: projection.signatureHash, + summaryHash: projection.overrideSummaryHash + }); + } + latestProjection = projection; + if (history.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxHistoryRecords) { + throw new Error("Native repair trajectory history exceeds its record boundary"); + } + } + return { history, latestProjection }; +} +function rebuildNativeRepairHistory(options) { + return projectNativeRepairHistory(options).history; +} +function inspectLatestNativeRepairProjection(options) { + const projection = projectNativeRepairHistory(options).latestProjection; + return projection ? { ...projection } : null; +} +function acceptLatestNativeRepairOverride(options) { + const projected = projectNativeRepairHistory(options); + const request = record5(options.override, "Native repair override request"); + exactKeys(request, ["expectedSignatureHash", "summary"], "Native repair override request"); + const expectedSignatureHash = hash4( + request.expectedSignatureHash, + "Native repair override expected signature" + ); + if (typeof request.summary !== "string") { + throw new Error("Native repair override summary is invalid"); + } + if (projected.latestProjection?.disposition === "hard-stop") { + throw new Error("Native repair hard stop cannot be overridden"); + } + if (projected.latestProjection?.disposition !== "manual-stop" || projected.latestProjection.signatureHash !== expectedSignatureHash) { + throw new Error("Native repair override does not match the latest manual stop"); + } + if (projected.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === expectedSignatureHash + )) { + throw new Error("Native repair signature was already overridden"); + } + const overrideSummaryHash = hashNativeRepairOverrideSummary(request.summary); + return { + history: projected.history, + eventProjection: { + signatureHash: expectedSignatureHash, + disposition: "continue", + overrideSummaryHash + } + }; +} +function nativeRepairFailureFacts(input) { + const envelope = parseNativeVerificationEvidenceEnvelope(input.envelope); + const bundle = parseNativeImplementationScopeBundle(input.implementationScope); + if (envelope.result !== "fail") { + throw new Error("Native repair stagnation requires a failed verification envelope"); + } + if (envelope.contractHash !== bundle.scope.contractHash || envelope.implementationScopeHash !== bundle.scope.scopeHash) { + throw new Error("Native repair evidence does not match the implementation scope authority"); + } + const tokens = normalizeNativeRepairFailureTokens({ + categories: input.categories, + failedCheckIds: input.failedCheckIds + }); + return { + contractHash: bundle.scope.contractHash, + implementationScopeHash: nativeRepairScopeHash(bundle), + artifactSnapshotHash: bundle.scope.currentProjectionHash, + categories: tokens.categories, + failedCheckIds: tokens.failedCheckIds + }; +} +function projectionForDecision(decision, overrideSummaryHash) { + return { + signatureHash: decision.signature.signatureHash, + disposition: decision.disposition, + overrideSummaryHash + }; +} +function runtimeContext(input) { + return { + facts: nativeRepairFailureFacts(input), + history: rebuildNativeRepairHistory(input) + }; +} +function inspectNativeRepairFailure(input) { + const context = runtimeContext(input); + const decision = decideNativeRepairStagnation(context); + return { + ...context, + decision, + eventProjection: projectionForDecision(decision, null) + }; +} + +// domains/comet-native/native-runtime-package.ts +var NATIVE_RUNTIME_PACKAGE = { + root: "/comet/native-runtime", + packageKind: "runtime", + definition: { + apiVersion: "comet/v1alpha1", + kind: "Skill", + metadata: { + name: "comet-native-runtime", + version: "3", + description: "Comet-owned state runtime for the Native workflow." + }, + goal: { + statement: "Advance a Native change only after its current guard passes.", + inputs: [], + outputs: [], + success: ["The Native change and Run state agree on the next phase."] + }, + orchestration: { + mode: "deterministic", + entry: "shape", + steps: [ + { id: "shape", action: { type: "checkpoint" }, next: "build" }, + { id: "build", action: { type: "checkpoint" }, next: "verify" }, + { id: "verify", action: { type: "checkpoint" }, next: "archive" }, + { id: "archive", action: { type: "checkpoint" } } + ] + }, + skills: [], + agents: [], + tools: [] + }, + guardrails: { + allowedSkills: [], + allowedAgents: [], + allowedTools: [], + // Native uses the evidence-bound repair episode budget below the engine seam. The generic + // counter remains an action ID source, not a user-visible permanent stop for long-lived changes. + maxIterations: Number.MAX_SAFE_INTEGER, + maxRetriesPerAction: 2, + confirmationRequiredFor: [] + }, + evals: [] +}; +var NATIVE_RUNTIME_HASH = sha256Text("comet-native-runtime:v3:semantic-repair-budget"); +var NATIVE_LEGACY_RUNTIME_IDENTITIES = [ + { + skillVersion: "2", + skillHash: sha256Text("comet-native-runtime:v2:max-iterations-32") + }, + { + skillVersion: "1", + skillHash: sha256Text("comet-native-runtime:v1") + } +]; +function isCompatibleNativeRuntimeIdentity(run) { + return run.skillVersion === NATIVE_RUNTIME_PACKAGE.definition.metadata.version && run.skillHash === NATIVE_RUNTIME_HASH || NATIVE_LEGACY_RUNTIME_IDENTITIES.some( + (identity) => identity.skillVersion === run.skillVersion && identity.skillHash === run.skillHash + ); +} +var nativePhaseResolver = { + resolveStep({ pkg, state }) { + return pkg.definition.orchestration.steps?.find((step) => step.id === state.currentStep); + }, + resolveNext({ step, outcome }) { + if (step.id === "verify" && outcome.state?.verification_result === "fail") return "build"; + return step.next ?? null; + } +}; + +// domains/comet-native/native-trajectory.ts +async function appendNativeTrajectoryEvent(options) { + const trajectory = await readNativeTrajectory(options.changeDir, options.run.trajectoryRef); + const event = { + sequence: trajectory.length + 1, + timestamp: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + type: options.type, + runId: options.run.runId, + data: options.data + }; + await appendNativeTrajectory(options.changeDir, options.run.trajectoryRef, event); + return event; +} +async function writeNativeCheckpoint2(options) { + const checkpoint = { + runId: options.run.runId, + stateVersion: options.run.iteration, + trajectoryOffset: options.trajectoryOffset, + contextHash: null, + artifactsHash: sha256Text(options.evidenceHash), + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString() + }; + await writeNativeCheckpoint(options.changeDir, options.run.checkpointRef, checkpoint); + return checkpoint; +} + +// domains/comet-native/native-transition-evidence.ts +function nativeAdvanceEvidenceHash(evidence) { + return sha256Text( + JSON.stringify({ + summary: evidence.summary, + confirmed: evidence.confirmed ?? false, + artifacts: [...evidence.artifacts ?? []].sort(), + noCodeReason: evidence.noCodeReason ?? null, + verificationResult: evidence.verificationResult ?? null, + verificationReport: evidence.verificationReport ?? null, + verificationReceipt: evidence.verificationReceipt ?? null, + allowPartialScopeHash: evidence.allowPartialScopeHash ?? null, + partialReason: evidence.partialReason ?? null, + repairFailureCategories: [...evidence.repairFailureCategories ?? []].sort(), + repairFailedCheckIds: [...evidence.repairFailedCheckIds ?? []].sort(), + repairOverrideSignature: evidence.repairOverrideSignature ?? null, + repairOverrideSummary: evidence.repairOverrideSummary ?? null + }) + ); +} + +// domains/comet-native/native-transition-journal.ts +var COMMON_JOURNAL_KEYS = [ + "schema", + "id", + "change", + "evidenceHash", + "createdAt", + "previousState", + "nextState", + "previousRun", + "nextRun", + "eventData" +]; +var LEGACY_JOURNAL_KEYS = new Set(COMMON_JOURNAL_KEYS); +var V2_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + ...COMMON_JOURNAL_KEYS, + "minimum_runtime_version", + "revision" +]); +var CURRENT_JOURNAL_KEYS = /* @__PURE__ */ new Set([...V2_JOURNAL_KEYS, "operation"]); +var NATIVE_TRANSITION_JOURNAL_MAX_BYTES = 512 * 1024; +var REQUIRED_TRANSITION_EVENT_DATA_KEYS = /* @__PURE__ */ new Set([ + "previousPhase", + "nextPhase", + "evidenceHash", + "summary", + "artifacts", + "noCodeReason", + "verificationResult" +]); +var TRANSITION_EVENT_DATA_KEYS = /* @__PURE__ */ new Set([ + ...REQUIRED_TRANSITION_EVENT_DATA_KEYS, + "implementationScopeHash", + "repairScopeHash", + "repairStagnation" +]); +var NativeTransitionMigrationRequiredError = class extends Error { + constructor(change) { + super(`Native transition for ${change} requires doctor migration before recovery`); + this.change = change; + this.name = "NativeTransitionMigrationRequiredError"; + } + change; + code = "native-transition-migration-required"; +}; +function nativeTransitionJournalFile(paths, name) { + return path23.join(nativeChangeDir(paths, name), "runtime", "transition.json"); +} +function nativeTransitionLockName(name) { + return `transition-${name}`; +} +async function acquireNativeTransitionLock(paths, name, operation) { + const lockName2 = nativeTransitionLockName(name); + return acquireNativeLock(paths, lockName2, operation); +} +async function withNativeTransitionLock(paths, name, operation, work) { + const lock = await acquireNativeTransitionLock(paths, name, operation); + try { + return await work(); + } finally { + await releaseNativeLock(lock); + } +} +function journalRecord(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native transition journal must be an object"); + } + return value; +} +function rejectUnknownJournalFields(journal, known) { + const unknown = Object.keys(journal).find((key) => !known.has(key)); + if (unknown) throw new Error(`Native transition journal contains unknown field: ${unknown}`); +} +function positiveInteger4(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function sameValue(left, right) { + return isDeepStrictEqual(left, right); +} +function parseTransitionEventData(value, evidenceHash) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native transition journal event data is invalid"); + } + const record8 = value; + const keys = Object.keys(record8); + const unknown = keys.find((key) => !TRANSITION_EVENT_DATA_KEYS.has(key)); + const missing = [...REQUIRED_TRANSITION_EVENT_DATA_KEYS].find( + (key) => !Object.hasOwn(record8, key) + ); + const expectedSize = REQUIRED_TRANSITION_EVENT_DATA_KEYS.size + (Object.hasOwn(record8, "implementationScopeHash") ? 1 : 0) + (Object.hasOwn(record8, "repairScopeHash") ? 1 : 0) + (Object.hasOwn(record8, "repairStagnation") ? 1 : 0); + if (unknown || missing || keys.length !== expectedSize) { + throw new Error( + `Native transition journal event data keys are invalid${unknown ? `: ${unknown}` : missing ? `: missing ${missing}` : ""}` + ); + } + if (!["shape", "build", "verify", "archive"].includes(record8.previousPhase) || !["shape", "build", "verify", "archive"].includes(record8.nextPhase)) { + throw new Error("Native transition journal event phases are invalid"); + } + if (record8.evidenceHash !== evidenceHash) { + throw new Error("Native transition journal event evidence hash mismatch"); + } + assertNativeTrajectoryText(record8.summary, "Native transition journal event summary"); + if (redactNativeCredentialText(record8.summary) !== record8.summary) { + throw new Error("Native transition journal event summary contains unredacted credentials"); + } + if (!Array.isArray(record8.artifacts) || record8.artifacts.length > 128 || record8.artifacts.some( + (artifact) => typeof artifact !== "string" || artifact.length === 0 || Buffer.byteLength(artifact, "utf8") > 512 + )) { + throw new Error("Native transition journal event artifacts are invalid"); + } + if (record8.noCodeReason !== null) { + assertNativeTrajectoryText( + record8.noCodeReason, + "Native transition journal event no-code reason" + ); + if (redactNativeCredentialText(record8.noCodeReason) !== record8.noCodeReason) { + throw new Error( + "Native transition journal event no-code reason contains unredacted credentials" + ); + } + } + if (record8.verificationResult !== null && record8.verificationResult !== "pass" && record8.verificationResult !== "fail") { + throw new Error("Native transition journal event verification result is invalid"); + } + const implementationScopeHash = Object.hasOwn(record8, "implementationScopeHash") ? record8.implementationScopeHash : null; + if (implementationScopeHash !== null && (typeof implementationScopeHash !== "string" || !/^[a-f0-9]{64}$/.test(implementationScopeHash) || record8.previousPhase !== "build" && record8.previousPhase !== "verify")) { + throw new Error("Native transition journal implementation scope hash is invalid"); + } + const repairStagnation = Object.hasOwn(record8, "repairStagnation") ? parseNativeRepairTrajectoryProjection(record8.repairStagnation) : null; + if (repairStagnation) { + const failureProjection = repairStagnation.overrideSummaryHash === null; + if (failureProjection && (record8.previousPhase !== "verify" || record8.nextPhase !== "build" || record8.verificationResult !== "fail") || !failureProjection && (record8.previousPhase !== "build" || record8.nextPhase !== "verify" || record8.verificationResult !== null)) { + throw new Error("Native transition journal repair projection does not match its phase"); + } + } + const repairScopeHash = Object.hasOwn(record8, "repairScopeHash") ? record8.repairScopeHash : null; + if (repairScopeHash !== null && (typeof repairScopeHash !== "string" || !/^[a-f0-9]{64}$/.test(repairScopeHash) || record8.previousPhase !== "build" && record8.previousPhase !== "verify" || implementationScopeHash === null)) { + throw new Error("Native transition journal repair scope hash is invalid"); + } + return { + previousPhase: record8.previousPhase, + nextPhase: record8.nextPhase, + evidenceHash, + summary: record8.summary, + artifacts: [...record8.artifacts], + noCodeReason: record8.noCodeReason, + verificationResult: record8.verificationResult, + ...implementationScopeHash ? { implementationScopeHash } : {}, + ...repairScopeHash ? { repairScopeHash } : {}, + ...repairStagnation ? { repairStagnation } : {} + }; +} +function parseLegacyTransitionEventData(value, evidenceHash) { + if (value && typeof value === "object" && !Array.isArray(value)) { + const record8 = value; + const keys = Object.keys(record8); + const legacyRebaseKeys = /* @__PURE__ */ new Set([ + "previousPhase", + "nextPhase", + "evidenceHash", + "summary", + "reason" + ]); + if (keys.length === legacyRebaseKeys.size && keys.every((key) => legacyRebaseKeys.has(key)) && record8.reason === "spec-rebase") { + return parseTransitionEventData( + { + previousPhase: record8.previousPhase, + nextPhase: record8.nextPhase, + evidenceHash: record8.evidenceHash, + summary: record8.summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }, + evidenceHash + ); + } + } + return parseTransitionEventData(value, evidenceHash); +} +function validateJournalEnvelope(journal, expectedName, legacyEventData = false) { + if (journal.change !== expectedName) throw new Error("Native transition journal change mismatch"); + if (typeof journal.id !== "string" || journal.id.length === 0) { + throw new Error("Native transition journal id is invalid"); + } + if (typeof journal.evidenceHash !== "string" || !/^[a-f0-9]{64}$/u.test(journal.evidenceHash)) { + throw new Error("Native transition journal evidence hash is invalid"); + } + if (typeof journal.createdAt !== "string" || Number.isNaN(Date.parse(journal.createdAt))) { + throw new Error("Native transition journal timestamp is invalid"); + } + const eventData = legacyEventData ? parseLegacyTransitionEventData(journal.eventData, journal.evidenceHash) : parseTransitionEventData(journal.eventData, journal.evidenceHash); + const nextRun = parseNativeStoredRunStateValue(journal.nextRun); + const previousRun = journal.previousRun === null ? null : parseNativeStoredRunStateValue(journal.previousRun); + return { + id: journal.id, + evidenceHash: journal.evidenceHash, + createdAt: journal.createdAt, + previousRun, + nextRun, + eventData + }; +} +function assertNativeRunMetadata(run, label, allowCompatibleLegacyIdentity = false) { + if (run.skill !== NATIVE_RUNTIME_PACKAGE.definition.metadata.name || (allowCompatibleLegacyIdentity ? !isCompatibleNativeRuntimeIdentity(run) : run.skillVersion !== NATIVE_RUNTIME_PACKAGE.definition.metadata.version || run.skillHash !== NATIVE_RUNTIME_HASH) || run.orchestration !== NATIVE_RUNTIME_PACKAGE.definition.orchestration.mode || run.pendingRef !== NATIVE_RUN_STORAGE.pendingRef || run.trajectoryRef !== NATIVE_RUN_STORAGE.trajectoryRef || run.contextRef !== NATIVE_RUN_STORAGE.contextRef || run.artifactsRef !== NATIVE_RUN_STORAGE.artifactsRef || run.checkpointRef !== NATIVE_RUN_STORAGE.checkpointRef) { + throw new Error(`Native transition journal ${label} metadata or storage refs are invalid`); + } +} +function runInvariantProjection(run) { + return { + runId: run.runId, + skill: run.skill, + skillVersion: run.skillVersion, + skillHash: run.skillHash, + orchestration: run.orchestration, + pendingRef: run.pendingRef, + trajectoryRef: run.trajectoryRef, + contextRef: run.contextRef, + artifactsRef: run.artifactsRef, + checkpointRef: run.checkpointRef, + retries: run.retries + }; +} +function assertCommittableRun(run, label) { + if (run.status !== "running") { + throw new Error(`Native transition journal ${label} status must be running`); + } + if (run.pending !== null) { + throw new Error(`Native transition journal ${label} pending action must be null`); + } +} +function validateTransitionRunSemantics(previousState, nextState, envelope, allowCompatibleLegacyIdentity = false) { + const { previousRun, nextRun } = envelope; + assertNativeRunMetadata( + nextRun, + "next Run", + allowCompatibleLegacyIdentity || previousRun !== null + ); + assertCommittableRun(nextRun, "next Run"); + if (nextRun.runId !== nextState.run_id || nextRun.currentStep !== nextState.phase) { + throw new Error("Native transition journal next Run does not match the next change state"); + } + if (previousRun === null) { + if (previousState.run_id !== null || previousState.phase !== "shape" || nextRun.iteration !== 1 || Object.keys(nextRun.retries).length !== 0) { + throw new Error("Native transition journal first Run transition is invalid"); + } + return; + } + assertNativeRunMetadata(previousRun, "previous Run", true); + assertCommittableRun(previousRun, "previous Run"); + if (previousState.run_id !== previousRun.runId || previousRun.currentStep !== previousState.phase || nextState.run_id !== previousRun.runId) { + throw new Error("Native transition journal previous Run does not match the change state"); + } + if (nextRun.iteration !== previousRun.iteration + 1) { + throw new Error("Native transition journal next Run iteration must advance exactly once"); + } + if (!isDeepStrictEqual(runInvariantProjection(previousRun), runInvariantProjection(nextRun))) { + throw new Error("Native transition journal Run identity or storage invariants changed"); + } +} +function specRebaseEvidenceHash(name, summary, specChanges) { + return sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: name, + summary, + specChanges + }) + ); +} +function inferredLegacyTransitionOperation(previousState, nextState, event) { + return previousState.phase !== "shape" && nextState.phase === "build" && event.verificationResult === null ? "spec-rebase" : "advance"; +} +function currentStateRefs(state) { + return "implementation_scope" in state ? { + implementation_scope: state.implementation_scope, + verification_evidence: state.verification_evidence, + partial_allowance: state.partial_allowance + } : null; +} +function validateTransitionStateSemantics(previousState, nextState, envelope, operation, legacyOperation = false) { + const event = envelope.eventData; + const previousRefs = currentStateRefs(previousState); + const nextRefs = currentStateRefs(nextState); + if (event.previousPhase !== previousState.phase || event.nextPhase !== nextState.phase) { + throw new Error("Native transition journal event phases do not match its states"); + } + if (previousState.archived || nextState.archived) { + throw new Error("Native transition journal cannot mutate an archived change"); + } + if (operation === "evidence-retreat") { + const sameIdentity2 = previousState.name === nextState.name && previousState.language === nextState.language && previousState.brief === nextState.brief && previousState.approval === nextState.approval && ("approved_contract_hash" in previousState ? "approved_contract_hash" in nextState && previousState.approved_contract_hash === nextState.approved_contract_hash : !("approved_contract_hash" in nextState)) && previousState.created_at === nextState.created_at && previousState.run_id === nextState.run_id && isDeepStrictEqual(previousState.spec_changes, nextState.spec_changes); + const expectedHash = nativeAdvanceEvidenceHash({ summary: event.summary }); + const validSourcePhase = previousState.phase === "archive" && previousState.verification_result === "pass" || previousState.phase === "verify" && previousState.verification_result === "pending"; + if (!validSourcePhase || nextState.phase !== "build" || nextState.verification_result !== "pending" || nextState.verification_report !== null || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || envelope.evidenceHash !== expectedHash || !sameIdentity2 || previousRefs === null || nextRefs === null || nextRefs.implementation_scope !== null || nextRefs.verification_evidence !== null || nextRefs.partial_allowance !== null) { + throw new Error("Native transition journal evidence retreat semantics are invalid"); + } + return; + } + if (operation === "spec-rebase") { + const currentHash = specRebaseEvidenceHash( + previousState.name, + event.summary, + nextState.spec_changes + ); + const legacyHash = sha256Text(`spec-rebase:${previousState.name}:${event.summary}`); + if (previousState.phase === "shape" || nextState.phase !== "build" || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || nextState.verification_result !== "pending" || nextState.verification_report !== null || !legacyOperation && envelope.evidenceHash !== currentHash || legacyOperation && envelope.evidenceHash !== currentHash && envelope.evidenceHash !== legacyHash || "implementation_scope" in nextState && (nextState.implementation_scope !== null || nextState.verification_evidence !== null || nextState.partial_allowance !== null)) { + throw new Error("Native transition journal spec rebase semantics are invalid"); + } + return; + } + if (previousState.phase === "shape") { + if (nextState.phase !== "build" || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || previousState.verification_result !== "pending" || nextState.verification_result !== "pending" || previousState.verification_report !== null || nextState.verification_report !== null || previousRefs !== null && nextRefs !== null && (previousRefs.implementation_scope !== null || previousRefs.verification_evidence !== null || previousRefs.partial_allowance !== null || nextRefs.implementation_scope !== null || nextRefs.verification_evidence !== null || nextRefs.partial_allowance !== null)) { + throw new Error("Native transition journal Shape to Build semantics are invalid"); + } + return; + } + if (previousState.phase === "build") { + if (nextState.phase !== "verify" || event.verificationResult !== null || event.artifacts.length === 0 && event.noCodeReason === null || nextState.verification_result !== "pending" || nextState.verification_report !== null || previousRefs !== null && nextRefs !== null && (nextRefs.implementation_scope === null || nextRefs.verification_evidence !== null || nextRefs.implementation_scope !== previousRefs.implementation_scope && nextRefs.partial_allowance !== null && nextRefs.partial_allowance === previousRefs.partial_allowance)) { + throw new Error("Native transition journal Build to Verify semantics are invalid"); + } + return; + } + if (previousState.phase === "verify") { + const expectedNext = event.verificationResult === "pass" ? "archive" : "build"; + if (event.verificationResult !== "pass" && event.verificationResult !== "fail" || nextState.phase !== expectedNext || event.artifacts.length !== 0 || event.noCodeReason !== null || nextState.verification_result !== event.verificationResult || nextState.verification_report === null || previousRefs !== null && nextRefs !== null && (previousRefs.implementation_scope === null || nextRefs.implementation_scope !== previousRefs.implementation_scope || nextRefs.partial_allowance !== previousRefs.partial_allowance || previousRefs.verification_evidence !== null || nextRefs.verification_evidence === null)) { + throw new Error("Native transition journal Verify outcome semantics are invalid"); + } + return; + } + throw new Error("Native transition journal cannot advance from Archive"); +} +function parseNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, CURRENT_JOURNAL_KEYS); + if (journal.schema !== NATIVE_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_TRANSITION_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger4( + journal.minimum_runtime_version, + "Native transition minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native transition requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native transition ${NATIVE_TRANSITION_SCHEMA} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger4(journal.revision, "Native transition revision"); + if (revision !== 1) throw new Error("Native transition journal revision must be 1"); + if (journal.operation !== "advance" && journal.operation !== "spec-rebase" && journal.operation !== "evidence-retreat") { + throw new Error("Native transition journal operation is invalid"); + } + const envelope = validateJournalEnvelope(journal, expectedName); + const previousState = parseNativeChangeValue(journal.previousState); + const nextState = parseNativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native transition journal state mismatch"); + } + validateTransitionStateSemantics(previousState, nextState, envelope, journal.operation); + validateTransitionRunSemantics(previousState, nextState, envelope); + if (nextState.revision !== previousState.revision + 1) { + throw new Error("Native transition journal state revision must advance exactly once"); + } + return { + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + operation: journal.operation, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function parseLegacyNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, LEGACY_JOURNAL_KEYS); + if (journal.schema !== NATIVE_LEGACY_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_LEGACY_TRANSITION_SCHEMA}`); + } + const envelope = validateJournalEnvelope(journal, expectedName, true); + const previousState = parseLegacyNativeChangeValue(journal.previousState); + const nextState = parseLegacyNativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native transition journal state mismatch"); + } + validateTransitionStateSemantics( + previousState, + nextState, + envelope, + inferredLegacyTransitionOperation(previousState, nextState, envelope.eventData), + true + ); + validateTransitionRunSemantics(previousState, nextState, envelope, true); + return { + schema: NATIVE_LEGACY_TRANSITION_SCHEMA, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function parseV2NativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, V2_JOURNAL_KEYS); + if (journal.schema !== NATIVE_V2_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_V2_TRANSITION_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger4( + journal.minimum_runtime_version, + "Native v2 transition minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error( + `Native transition ${NATIVE_V2_TRANSITION_SCHEMA} minimum_runtime_version must be 2` + ); + } + const revision = positiveInteger4(journal.revision, "Native v2 transition revision"); + if (revision !== 1) throw new Error("Native v2 transition journal revision must be 1"); + const envelope = validateJournalEnvelope(journal, expectedName, true); + const previousState = parseV2NativeChangeValue(journal.previousState); + const nextState = parseV2NativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native v2 transition journal state mismatch"); + } + validateTransitionStateSemantics( + previousState, + nextState, + envelope, + inferredLegacyTransitionOperation(previousState, nextState, envelope.eventData), + true + ); + validateTransitionRunSemantics(previousState, nextState, envelope, true); + if (nextState.revision !== previousState.revision + 1) { + throw new Error("Native v2 transition journal state revision must advance exactly once"); + } + return { + schema: NATIVE_V2_TRANSITION_SCHEMA, + minimum_runtime_version: 2, + revision, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function inspectNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + if (journal.schema === NATIVE_TRANSITION_SCHEMA) { + return { status: "current", journal: parseNativeTransitionJournalValue(journal, expectedName) }; + } + if (journal.schema === NATIVE_LEGACY_TRANSITION_SCHEMA) { + return { + status: "migration-required", + journal: parseLegacyNativeTransitionJournalValue(journal, expectedName) + }; + } + if (journal.schema === NATIVE_V2_TRANSITION_SCHEMA) { + return { + status: "migration-required", + journal: parseV2NativeTransitionJournalValue(journal, expectedName) + }; + } + throw new Error(`Unsupported Native transition journal schema: ${String(journal.schema)}`); +} +async function inspectPendingNativeTransitionSchema(paths, name) { + const file = nativeTransitionJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_TRANSITION_JOURNAL_MAX_BYTES, + label: `Native transition journal ${name}` + }); + return inspectNativeTransitionJournalValue(JSON.parse(snapshot2.bytes.toString("utf8")), name); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function inspectPendingNativeTransition(paths, name) { + const inspection = await inspectPendingNativeTransitionSchema(paths, name); + if (!inspection) return null; + if (inspection.status === "migration-required") { + throw new NativeTransitionMigrationRequiredError(name); + } + return inspection.journal; +} +async function prepareNativeTransition(options) { + if (await hasPendingNativeSchemaMigration(options.paths, options.nextState.name)) { + throw new Error( + `Native schema migration is incomplete for ${options.nextState.name}; run doctor --repair` + ); + } + await assertNativeTrajectoryHealthy(options.paths, options.nextState.name); + const journal = { + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + operation: options.operation ?? "advance", + id: options.transitionId?.() ?? randomUUID3(), + change: options.nextState.name, + evidenceHash: options.evidenceHash, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + previousState: options.previousState, + nextState: options.nextState, + previousRun: options.previousRun, + nextRun: options.nextRun, + eventData: options.eventData + }; + const validated = parseNativeTransitionJournalValue(journal, journal.change); + const file = nativeTransitionJournalFile(options.paths, validated.change); + await resolveContainedNativePath(options.paths.nativeRoot, file); + if (await inspectPendingNativeTransition(options.paths, validated.change)) { + throw new Error(`Native transition recovery is already pending for ${validated.change}`); + } + await atomicWriteJson(file, validated); + return validated; +} +function assertRunRecoverySource(actual, journal) { + if (sameValue(actual, journal.nextRun)) return "next"; + if (journal.previousRun === null) { + if (actual === null) return "previous"; + } else if (sameValue(actual, journal.previousRun)) { + return "previous"; + } + throw new Error( + `Native transition Run content changed for ${journal.change}; recovery journal was preserved` + ); +} +function assertChangeRecoverySource(actual, journal) { + if (sameValue(actual, journal.nextState)) return "next"; + if (sameValue(actual, journal.previousState)) return "previous"; + throw new Error( + `Native transition change content changed for ${journal.change}; recovery journal was preserved` + ); +} +function expectedTrajectoryEvent(options) { + const { journal } = options; + return trajectoryEventForComparison({ + sequence: options.sequence, + timestamp: journal.createdAt, + type: options.type, + runId: journal.nextRun.runId, + data: options.type === "run_started" ? { + runtime: "comet-native", + phase: journal.previousState.phase, + transitionId: journal.id + } : { ...journal.eventData, transitionId: journal.id } + }); +} +function trajectoryEventForComparison(event) { + return { + sequence: event.sequence, + timestamp: event.timestamp, + type: event.type, + runId: event.runId, + data: event.data + }; +} +function inspectExistingTransitionEvents(trajectory, journal) { + const collisions = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.transitionId === journal.id); + const started = collisions.filter(({ event }) => event.type === "run_started"); + const transitioned = collisions.filter(({ event }) => event.type === "state_transitioned"); + if (collisions.length !== started.length + transitioned.length || started.length > (journal.previousRun === null ? 1 : 0) || transitioned.length > 1 || journal.previousRun === null && transitioned.length === 1 && (started.length !== 1 || started[0].index >= transitioned[0].index)) { + throw new Error( + `Native trajectory transition id collision for ${journal.change}; recovery journal was preserved` + ); + } + for (const item of [...started, ...transitioned]) { + const expected = expectedTrajectoryEvent({ + sequence: item.index + 1, + journal, + type: item.event.type + }); + if (!sameValue(trajectoryEventForComparison(item.event), expected)) { + throw new Error( + `Native trajectory event changed for transition ${journal.id}; recovery journal was preserved` + ); + } + } + return { + started: started[0]?.event ?? null, + transitioned: transitioned[0]?.event ?? null + }; +} +async function continueNativeTransitionLocked(paths, name, hooks) { + if (await hasPendingNativeSchemaMigration(paths, name)) { + throw new Error(`Native schema migration is incomplete for ${name}; run doctor --repair`); + } + await assertNativeTrajectoryHealthy(paths, name); + const journal = await inspectPendingNativeTransition(paths, name); + if (!journal) return null; + const changeDir = nativeChangeDir(paths, name); + const initialEvents = inspectExistingTransitionEvents( + await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef), + journal + ); + const [actualRun, actualChange] = await Promise.all([ + readNativeRunState(changeDir), + readNativeChange(paths, name) + ]); + const runSource = assertRunRecoverySource(actualRun, journal); + const changeSource = assertChangeRecoverySource(actualChange, journal); + if ((initialEvents.started || initialEvents.transitioned) && (runSource !== "next" || changeSource !== "next")) { + throw new Error( + `Native trajectory is ahead of transition state for ${journal.change}; recovery journal was preserved` + ); + } + if (runSource === "previous") { + await writeNativeRunState(changeDir, journal.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + if (!sameValue(await readNativeRunState(changeDir), journal.nextRun)) { + throw new Error( + `Native transition Run content changed while continuing ${journal.change}; recovery journal was preserved` + ); + } + if (changeSource === "previous") { + const persisted = await compareAndSwapNativeChangeLocked( + paths, + journal.nextState, + journal.previousState.revision + ); + if (!sameValue(persisted, journal.nextState)) { + throw new Error( + `Native transition change write diverged for ${journal.change}; recovery journal was preserved` + ); + } + await hooks?.afterChangeStateWritten?.(journal); + } + if (!sameValue(await readNativeChange(paths, name), journal.nextState)) { + throw new Error( + `Native transition change content changed while continuing ${journal.change}; recovery journal was preserved` + ); + } + const activeJournal = await inspectPendingNativeTransition(paths, name); + if (!activeJournal || !sameValue(activeJournal, journal)) { + throw new Error( + `Native transition journal changed while continuing ${journal.change}; it was preserved` + ); + } + const existingEvents = inspectExistingTransitionEvents( + await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef), + journal + ); + if (journal.previousRun === null) { + if (!existingEvents.started) { + await appendNativeTrajectoryEvent({ + changeDir, + run: journal.nextRun, + type: "run_started", + data: { + runtime: "comet-native", + phase: journal.previousState.phase, + transitionId: journal.id + }, + now: new Date(journal.createdAt) + }); + } + } + let event = existingEvents.transitioned; + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir, + run: journal.nextRun, + type: "state_transitioned", + data: { ...journal.eventData, transitionId: journal.id }, + now: new Date(journal.createdAt) + }); + } + await writeNativeCheckpoint2({ + changeDir, + run: journal.nextRun, + trajectoryOffset: event.sequence, + evidenceHash: journal.evidenceHash, + now: new Date(journal.createdAt) + }); + const [finalRun, finalChange] = await Promise.all([ + readNativeRunState(changeDir), + readNativeChange(paths, name) + ]); + assertRunRecoverySource(finalRun, { ...journal, previousRun: journal.nextRun }); + assertChangeRecoverySource(finalChange, { + ...journal, + previousState: journal.nextState + }); + const finalJournal = await inspectPendingNativeTransition(paths, name); + if (!finalJournal || !sameValue(finalJournal, journal)) { + throw new Error( + `Native transition journal changed while continuing ${journal.change}; it was not removed` + ); + } + await fs16.rm(nativeTransitionJournalFile(paths, name), { force: true }); + return journal.nextState; +} +async function continueNativeTransition(paths, name, hooks) { + return withNativeMutationLock( + paths, + `continue transition ${name}`, + () => withNativeTransitionLock( + paths, + name, + `continue transition ${name}`, + () => continueNativeTransitionLocked(paths, name, hooks) + ) + ); +} + +// domains/comet-native/native-verification-runtime.ts +import path28 from "node:path"; + +// domains/comet-native/native-acceptance.ts +import path24 from "node:path"; +var ACCEPTANCE_HASH_TAG = "comet.native.acceptance.v1"; +var ACCEPTANCE_ID_PATTERN = /^acceptance-[a-f0-9]{64}$/u; +var EVIDENCE_ENTRY_KEYS = /* @__PURE__ */ new Set(["acceptance_id", "evidence_refs", "skipped_reason"]); +var ACCEPTANCE_HASH_PATTERN = /^[a-f0-9]{64}$/u; +var ACCEPTANCE_CURSOR_PATTERN = /^native-acceptance-v1\.([a-f0-9]{64})\.([0-9a-z]+)\.([a-f0-9]{64})$/u; +var NATIVE_ACCEPTANCE_PAGE_LIMITS = Object.freeze({ + maxItems: 16, + maxTextBytes: 512, + maxContextItems: 4, + maxContextItemBytes: 256, + maxSerializedBytes: 32 * 1024 +}); +var NATIVE_ACCEPTANCE_LIMITS = Object.freeze({ + maxCriteria: 1024 +}); +var NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER = ""; +var NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER = ""; +function truncateUtf8(value, maxBytes) { + if (Buffer.byteLength(value, "utf8") <= maxBytes) return { value, truncated: false }; + let result2 = ""; + let bytes = 0; + for (const character of value) { + const characterBytes = Buffer.byteLength(character, "utf8"); + if (bytes + characterBytes > maxBytes) break; + result2 += character; + bytes += characterBytes; + } + return { value: result2, truncated: true }; +} +function acceptanceCursor(acceptanceHash, offset) { + const encodedOffset = offset.toString(36); + const cursorHash = canonicalHash("comet.native.acceptance-cursor.v1", { + acceptanceHash, + offset + }); + return `native-acceptance-v1.${acceptanceHash}.${encodedOffset}.${cursorHash}`; +} +function acceptanceOffset(options) { + if (!ACCEPTANCE_HASH_PATTERN.test(options.acceptanceHash)) { + throw new Error("Native acceptance page hash is invalid"); + } + if (options.cursor === void 0 || options.cursor === null) return 0; + const match = ACCEPTANCE_CURSOR_PATTERN.exec(options.cursor); + if (!match) throw new Error("Native acceptance cursor is invalid"); + if (match[1] !== options.acceptanceHash) { + throw new Error("Native acceptance cursor is stale"); + } + const offset = Number.parseInt(match[2], 36); + if (!Number.isSafeInteger(offset) || offset <= 0 || offset >= options.total || offset.toString(36) !== match[2]) { + throw new Error("Native acceptance cursor offset is invalid"); + } + if (match[3] !== canonicalHash("comet.native.acceptance-cursor.v1", { + acceptanceHash: options.acceptanceHash, + offset + })) { + throw new Error("Native acceptance cursor integrity check failed"); + } + return offset; +} +function acceptanceProjection(criterion2) { + const text = truncateUtf8(criterion2.text, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxTextBytes); + const projectedContext = criterion2.context.slice(0, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxContextItems).map((entry2) => truncateUtf8(entry2, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxContextItemBytes)); + return { + id: criterion2.id, + kind: criterion2.kind, + source: criterion2.source, + context: projectedContext.map((entry2) => entry2.value), + text: text.value, + contextTruncated: criterion2.context.length > projectedContext.length || projectedContext.some((entry2) => entry2.truncated), + textTruncated: text.truncated + }; +} +function projectNativeAcceptancePage(options) { + const offset = acceptanceOffset({ + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + cursor: options.cursor + }); + const items = []; + const remaining = options.criteria.slice(offset, offset + NATIVE_ACCEPTANCE_PAGE_LIMITS.maxItems); + for (const criterion2 of remaining) { + const candidate = [...items, acceptanceProjection(criterion2)]; + const nextOffset2 = offset + candidate.length; + const trial = { + schema: "comet.native.acceptance-page.v1", + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + offset, + items: candidate, + nextCursor: nextOffset2 < options.criteria.length ? acceptanceCursor(options.acceptanceHash, nextOffset2) : null, + limits: { ...NATIVE_ACCEPTANCE_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(trial), "utf8") > NATIVE_ACCEPTANCE_PAGE_LIMITS.maxSerializedBytes) { + if (items.length === 0) { + throw new Error("Native acceptance criterion exceeds the page serialization budget"); + } + break; + } + items.push(candidate.at(-1)); + } + if (options.criteria.length > 0 && items.length === 0) { + throw new Error("Native acceptance page could not project its next criterion"); + } + const nextOffset = offset + items.length; + const page = { + schema: "comet.native.acceptance-page.v1", + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + offset, + items, + nextCursor: nextOffset < options.criteria.length ? acceptanceCursor(options.acceptanceHash, nextOffset) : null, + limits: { ...NATIVE_ACCEPTANCE_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(page), "utf8") > NATIVE_ACCEPTANCE_PAGE_LIMITS.maxSerializedBytes) { + throw new Error("Native acceptance page exceeds its serialization budget"); + } + return page; +} +function markdownHeading(line) { + const match = /^ {0,3}(#{1,6})[ \t]+(.+?)[ \t]*$/u.exec(line); + if (!match) return null; + return { + level: match[1].length, + text: match[2].replace(/[ \t]+#+[ \t]*$/u, "").trim() + }; +} +function nextFenceState(line, current) { + const match = /^ {0,3}(`{3,}|~{3,})(.*)$/u.exec(line); + if (!match) return current; + const marker = match[1][0]; + if (current === null) return { marker, length: match[1].length }; + if (marker === current.marker && match[1].length >= current.length && match[2].trim().length === 0) { + return null; + } + return current; +} +function* iterateScannedMarkdown(markdown) { + let fence = null; + let htmlComment = false; + let htmlTag = null; + const normalized = markdown.replace(/\r\n?/gu, "\n"); + let start = 0; + let index = 0; + while (start <= normalized.length) { + const end = normalized.indexOf("\n", start); + const line = end === -1 ? normalized.slice(start) : normalized.slice(start, end); + const body = fence === null && !htmlComment && htmlTag === null; + yield { line, body, index }; + index += 1; + if (fence !== null) { + fence = nextFenceState(line, fence); + } else if (htmlComment) { + if (line.includes("-->")) htmlComment = false; + } else if (htmlTag !== null) { + if (new RegExp(``, "iu").test(line)) htmlTag = null; + } else { + const nextFence = nextFenceState(line, null); + if (nextFence !== null) { + fence = nextFence; + } else { + const trimmed = line.trimStart(); + if (trimmed.startsWith("")) { + htmlComment = true; + } else { + const htmlStart = /^<([A-Za-z][A-Za-z0-9-]*)\b[^>]*>/u.exec(trimmed); + if (htmlStart && !trimmed.startsWith("") && !new RegExp(``, "iu").test(trimmed)) { + htmlTag = htmlStart[1]; + } + } + } + } + if (end === -1) break; + start = end + 1; + } +} +function scanMarkdown(markdown) { + return [...iterateScannedMarkdown(markdown)].map(({ line, body }) => ({ line, body })); +} +function normalizeNativeAcceptanceText(value) { + return value.normalize("NFC").replace(/\s+/gu, " ").trim(); +} +function hasControlCharacter2(value) { + return Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }); +} +function criterion(kind, source, rawText, rawContext = []) { + const text = normalizeNativeAcceptanceText(rawText); + const normalizedSource = source.replaceAll("\\", "/").trim(); + const context = rawContext.map(normalizeNativeAcceptanceText); + if (text.length === 0) throw new Error(`${kind} acceptance criterion must not be empty`); + if (normalizedSource.length === 0) { + throw new Error(`${kind} acceptance criterion source must not be empty`); + } + return { + id: `acceptance-${canonicalHash(ACCEPTANCE_HASH_TAG, { + kind, + source: normalizedSource, + context, + text + })}`, + kind, + source: normalizedSource, + context, + text + }; +} +function uniqueCriteria(criteria, label) { + const seen = /* @__PURE__ */ new Set(); + for (const item of criteria) { + if (seen.has(item.id)) throw new Error(`${label} contains duplicate acceptance criteria`); + seen.add(item.id); + } + return criteria; +} +function acceptanceSectionBounds(markdown) { + let sectionStart = null; + let sectionEnd = null; + let matches = 0; + let lineCount = 0; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + lineCount = index + 1; + const heading = body ? markdownHeading(line) : null; + if (heading?.level !== 1) continue; + if (heading.text.toLocaleLowerCase("en-US") === "acceptance examples") { + matches += 1; + if (matches === 1) sectionStart = index + 1; + } else if (sectionStart !== null && sectionEnd === null) { + sectionEnd = index; + } + } + if (matches === 0 || sectionStart === null) return null; + if (matches !== 1) { + throw new Error("Brief must contain exactly one Acceptance examples section"); + } + return { start: sectionStart, end: sectionEnd ?? lineCount }; +} +function deriveBriefAcceptanceCriteria(markdown, source = "brief.md", maxCriteria = NATIVE_ACCEPTANCE_LIMITS.maxCriteria) { + if (!Number.isSafeInteger(maxCriteria) || maxCriteria < 0) { + throw new Error("Native brief acceptance budget is invalid"); + } + const section = acceptanceSectionBounds(markdown); + if (section === null) return []; + let topLevelIndent = null; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + if (index < section.start || index >= section.end) continue; + const listItem = body ? /^( {0,3})[-*+][ \t]+/u.exec(line) : null; + if (listItem === null) continue; + const indent = listItem[1].length; + topLevelIndent = topLevelIndent === null ? indent : Math.min(topLevelIndent, indent); + } + const items = []; + let active = null; + const pushActive = () => { + if (active === null) return; + if (items.length >= maxCriteria) { + throw new Error(`Native acceptance exceeds its ${maxCriteria}-criterion acceptance budget`); + } + items.push(active); + }; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + if (index < section.start || index >= section.end) continue; + const listItem = body ? /^( {0,3})[-*+][ \t]+(.*)$/u.exec(line) : null; + if (listItem && listItem[1].length === topLevelIndent) { + pushActive(); + active = [listItem[2]]; + } else if (active !== null) { + active.push(line); + } + } + pushActive(); + return uniqueCriteria( + items.map((lines) => criterion("brief-example", source, lines.join("\n"))), + "Brief" + ); +} +function deriveSpecAcceptanceCriteria(markdown, source = "spec.md", maxCriteria = NATIVE_ACCEPTANCE_LIMITS.maxCriteria) { + if (!Number.isSafeInteger(maxCriteria) || maxCriteria < 0) { + throw new Error("Native specification acceptance budget is invalid"); + } + const criteria = []; + const ancestry = []; + let active = null; + const flush = () => { + if (active === null) return; + if (criteria.length >= maxCriteria) { + throw new Error(`Native acceptance exceeds its ${maxCriteria}-criterion acceptance budget`); + } + criteria.push( + criterion("spec-scenario", source, [active.title, ...active.body].join("\n"), active.context) + ); + active = null; + }; + for (const { line, body } of iterateScannedMarkdown(markdown)) { + const heading = body ? markdownHeading(line) : null; + const scenario = heading ? /^Scenario\s*:\s*(.*)$/iu.exec(heading.text) : null; + if (scenario) { + flush(); + while (ancestry.at(-1) && ancestry.at(-1).level >= heading.level) ancestry.pop(); + const title = normalizeNativeAcceptanceText(scenario[1]); + if (title.length === 0) throw new Error("Scenario title must not be empty"); + active = { + level: heading.level, + title, + body: [], + context: ancestry.map((item) => item.text) + }; + } else if (heading) { + if (active !== null && heading.level <= active.level) flush(); + else if (active !== null) active.body.push(line); + while (ancestry.at(-1) && ancestry.at(-1).level >= heading.level) ancestry.pop(); + ancestry.push(heading); + } else if (active !== null && body) { + active.body.push(line); + } + } + flush(); + return uniqueCriteria(criteria, "Specification"); +} +function normalizeEvidenceRef(value, acceptanceId) { + const normalized = value.trim().replaceAll("\\", "/"); + if (normalized.length === 0 || hasControlCharacter2(normalized) || path24.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~|[A-Za-z][A-Za-z0-9+.-]*:)/u.test(normalized) || normalized.split("/").includes("..")) { + throw new Error(`Acceptance evidence ${acceptanceId} has an unsafe evidence ref`); + } + const portable = path24.posix.normalize(normalized); + if (portable === "." || portable === ".." || portable.startsWith("../")) { + throw new Error(`Acceptance evidence ${acceptanceId} has an unsafe evidence ref`); + } + if (portable.split("/").some( + (segment) => segment.toLowerCase() === ".git" || segment.toLowerCase().startsWith(".env") + )) { + throw new Error(`Acceptance evidence ${acceptanceId} references sensitive content`); + } + return portable; +} +function evidenceRecord2(value, index) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Acceptance evidence entry ${index} must be an object`); + } + const record8 = value; + const unknown = Object.keys(record8).filter((key) => !EVIDENCE_ENTRY_KEYS.has(key)); + if (unknown.length > 0) { + throw new Error( + `Acceptance evidence entry ${index} has unknown field(s): ${unknown.join(", ")}` + ); + } + return record8; +} +function validateEvidenceEntries(value) { + if (!Array.isArray(value)) + throw new Error("Native acceptance evidence block must be a JSON array"); + const seenIds = /* @__PURE__ */ new Set(); + return value.map((item, index) => { + const record8 = evidenceRecord2(item, index); + const acceptanceId = record8.acceptance_id; + if (typeof acceptanceId !== "string" || !ACCEPTANCE_ID_PATTERN.test(acceptanceId)) { + throw new Error(`Acceptance evidence entry ${index} has an invalid acceptance_id`); + } + if (seenIds.has(acceptanceId)) { + throw new Error(`Native acceptance evidence has duplicate acceptance_id: ${acceptanceId}`); + } + seenIds.add(acceptanceId); + if (!Array.isArray(record8.evidence_refs)) { + throw new Error(`Acceptance evidence ${acceptanceId} requires an evidence_refs array`); + } + const evidenceRefs = record8.evidence_refs.map((reference) => { + if (typeof reference !== "string" || reference.trim().length === 0) { + throw new Error(`Acceptance evidence ${acceptanceId} has a non-empty string requirement`); + } + return normalizeEvidenceRef(reference, acceptanceId); + }); + if (new Set(evidenceRefs).size !== evidenceRefs.length) { + throw new Error(`Acceptance evidence ${acceptanceId} has a duplicate evidence ref`); + } + let skippedReason; + if (Object.prototype.hasOwnProperty.call(record8, "skipped_reason")) { + if (typeof record8.skipped_reason !== "string" || record8.skipped_reason.trim().length === 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} skipped_reason must be a non-empty string` + ); + } + skippedReason = record8.skipped_reason.trim(); + } + if (evidenceRefs.length === 0 && skippedReason === void 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} requires evidence_refs or skipped_reason` + ); + } + if (evidenceRefs.length > 0 && skippedReason !== void 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} must not include both evidence and a skip` + ); + } + return { + acceptance_id: acceptanceId, + evidence_refs: evidenceRefs, + ...skippedReason === void 0 ? {} : { skipped_reason: skippedReason } + }; + }); +} +function parseNativeVerificationMachineBlock(markdown) { + const lines = scanMarkdown(markdown); + const invalidContextMarker = lines.some( + ({ line, body }) => !body && (line === NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER || line === NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER) + ); + if (invalidContextMarker) { + throw new Error("Native acceptance evidence markers must be in the Markdown body"); + } + const starts = lines.flatMap( + ({ line, body }, index) => body && line === NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER ? [index] : [] + ); + const ends = lines.flatMap( + ({ line, body }, index) => body && line === NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER ? [index] : [] + ); + if (starts.length !== 1 || ends.length !== 1) { + throw new Error("Verification must contain exactly one Native acceptance evidence block"); + } + if (starts[0] >= ends[0]) { + throw new Error("Native acceptance evidence markers are out of order"); + } + const payload = lines.slice(starts[0] + 1, ends[0]).map(({ line }) => line).join("\n").trim(); + if (payload.length === 0) throw new Error("Native acceptance evidence block is empty"); + let parsed; + try { + parsed = JSON.parse(payload); + } catch (error) { + throw new Error( + `Native acceptance evidence block is invalid JSON: ${error.message}`, + { cause: error } + ); + } + const validated = validateEvidenceEntries(parsed); + const canonicalPayload = canonicalEvidencePayload(validated); + if (payload !== canonicalPayload) { + throw new Error("Native acceptance evidence block must use canonical serialization"); + } + return validated; +} +function canonicalEvidencePayload(entries) { + const validated = validateEvidenceEntries([...entries]).map((entry2) => ({ ...entry2, evidence_refs: [...entry2.evidence_refs].sort() })).sort((left, right) => left.acceptance_id.localeCompare(right.acceptance_id)); + return JSON.stringify(validated, null, 2); +} +function serializeNativeVerificationMachineBlock(entries) { + return [ + NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER, + canonicalEvidencePayload(entries), + NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER + ].join("\n"); +} + +// domains/comet-native/native-check-receipt-storage.ts +import { constants as fsConstants4, promises as fs17 } from "node:fs"; +import path26 from "node:path"; + +// domains/comet-native/native-check-receipt-model.ts +import path25 from "node:path"; +var NATIVE_CHECK_RECEIPT_SCHEMA = "comet.native.check-receipt.v1"; +var NATIVE_CHECK_RECEIPT_HASH_TAG = "comet.native.check-receipt.v1"; +var NATIVE_CHECK_POLICY = "scoped-text-safety"; +var NATIVE_CHECK_POLICY_VERSION = 1; +var NATIVE_CHECK_LIMITS = Object.freeze({ + maxFiles: 256, + maxFileBytes: 1024 * 1024, + maxTotalBytes: 8 * 1024 * 1024, + maxIssues: 128 +}); +var HASH_PATTERN10 = /^[a-f0-9]{64}$/u; +var CHANGE_NAME_PATTERN3 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CHECKER_HASH_TAG = "comet.native.checker-policy.v1"; +var CHECK_INPUT_HASH_TAG = "comet.native.check-input.v1"; +var MAX_ISSUE_PATH_BYTES = 2048; +var NATIVE_CHECKER_HASH = canonicalHash(CHECKER_HASH_TAG, { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + limits: NATIVE_CHECK_LIMITS, + checks: ["conflict-marker", "space-before-tab", "trailing-whitespace"], + binaryHandling: "skip-and-count" +}); +var ISSUE_KINDS = /* @__PURE__ */ new Set([ + "conflict-marker", + "trailing-whitespace", + "space-before-tab", + "scope-mismatch", + "unsafe-file", + "scan-limit" +]); +var ISSUE_KIND_ORDER = [ + "conflict-marker", + "trailing-whitespace", + "space-before-tab", + "scope-mismatch", + "unsafe-file", + "scan-limit" +]; +var STALE_REASON_ORDER = [ + "contract-before-does-not-match-scope", + "implementation-before-does-not-match-scope", + "contract-changed-during-check", + "implementation-changed-during-check", + "contract-after-does-not-match-scope", + "implementation-after-does-not-match-scope" +]; +var STALE_REASONS = new Set(STALE_REASON_ORDER); +function record6(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactKeys2(value, expected, label) { + const keys = new Set(expected); + const unknown = Object.keys(value).filter((key) => !keys.has(key)); + const missing = expected.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function hash5(value, label) { + if (typeof value !== "string" || !HASH_PATTERN10.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function nonNegativeInteger2(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function positiveInteger5(value, label) { + const parsed = nonNegativeInteger2(value, label); + if (parsed < 1) throw new Error(`${label} must be a positive integer`); + return parsed; +} +function isoTimestamp2(value, label) { + if (typeof value !== "string") throw new Error(`${label} must be an ISO timestamp`); + const parsed = new Date(value); + if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) { + throw new Error(`${label} must be a canonical ISO timestamp`); + } + return value; +} +function projectRelativePath2(value, label) { + if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > MAX_ISSUE_PATH_BYTES || value.includes("\\") || value.includes("\0") || value.endsWith("/") || /^[a-zA-Z]:/u.test(value)) { + throw new Error(`${label} must be a bounded normalized project-relative path`); + } + const normalized = path25.posix.normalize(value); + if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../") || path25.posix.isAbsolute(normalized)) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function parseChecker(value) { + const checker = record6(value, "Native check receipt checker"); + exactKeys2(checker, ["policy", "version", "hash", "limits"], "Native check receipt checker"); + const limits = record6(checker.limits, "Native check receipt limits"); + exactKeys2( + limits, + ["maxFiles", "maxFileBytes", "maxTotalBytes", "maxIssues"], + "Native check receipt limits" + ); + if (checker.policy !== NATIVE_CHECK_POLICY || checker.version !== NATIVE_CHECK_POLICY_VERSION || checker.hash !== NATIVE_CHECKER_HASH || limits.maxFiles !== NATIVE_CHECK_LIMITS.maxFiles || limits.maxFileBytes !== NATIVE_CHECK_LIMITS.maxFileBytes || limits.maxTotalBytes !== NATIVE_CHECK_LIMITS.maxTotalBytes || limits.maxIssues !== NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt checker policy is unsupported"); + } + return { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + hash: NATIVE_CHECKER_HASH, + limits: { ...NATIVE_CHECK_LIMITS } + }; +} +function parseContract(value) { + const contract = record6(value, "Native check receipt contract"); + exactKeys2(contract, ["expectedHash", "beforeHash", "afterHash"], "Native check receipt contract"); + return { + expectedHash: hash5(contract.expectedHash, "Native check expected contract hash"), + beforeHash: hash5(contract.beforeHash, "Native check before contract hash"), + afterHash: hash5(contract.afterHash, "Native check after contract hash") + }; +} +function parseImplementation(value) { + const implementation = record6(value, "Native check receipt implementation"); + exactKeys2( + implementation, + ["scopeHash", "expectedSnapshotHash", "beforeSnapshotHash", "afterSnapshotHash"], + "Native check receipt implementation" + ); + return { + scopeHash: hash5(implementation.scopeHash, "Native check scope hash"), + expectedSnapshotHash: hash5( + implementation.expectedSnapshotHash, + "Native check expected snapshot hash" + ), + beforeSnapshotHash: hash5( + implementation.beforeSnapshotHash, + "Native check before snapshot hash" + ), + afterSnapshotHash: hash5(implementation.afterSnapshotHash, "Native check after snapshot hash") + }; +} +function parseCounts(value) { + const counts = record6(value, "Native check receipt counts"); + exactKeys2( + counts, + [ + "filesSelected", + "filesScanned", + "binaryFilesSkipped", + "bytesScanned", + "issueCount", + "recordedIssueCount" + ], + "Native check receipt counts" + ); + const parsed = { + filesSelected: nonNegativeInteger2(counts.filesSelected, "Native check filesSelected"), + filesScanned: nonNegativeInteger2(counts.filesScanned, "Native check filesScanned"), + binaryFilesSkipped: nonNegativeInteger2( + counts.binaryFilesSkipped, + "Native check binaryFilesSkipped" + ), + bytesScanned: nonNegativeInteger2(counts.bytesScanned, "Native check bytesScanned"), + issueCount: nonNegativeInteger2(counts.issueCount, "Native check issueCount"), + recordedIssueCount: nonNegativeInteger2( + counts.recordedIssueCount, + "Native check recordedIssueCount" + ) + }; + if (parsed.filesScanned + parsed.binaryFilesSkipped > parsed.filesSelected || parsed.filesScanned + parsed.binaryFilesSkipped > NATIVE_CHECK_LIMITS.maxFiles || parsed.bytesScanned > NATIVE_CHECK_LIMITS.maxTotalBytes || parsed.recordedIssueCount > parsed.issueCount || parsed.recordedIssueCount > NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt count accounting is invalid"); + } + return parsed; +} +function compareIssues(left, right) { + return left.path.localeCompare(right.path, "en") || left.line - right.line || ISSUE_KIND_ORDER.indexOf(left.kind) - ISSUE_KIND_ORDER.indexOf(right.kind); +} +function parseIssues(value) { + if (!Array.isArray(value) || value.length > NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt issues must be a bounded array"); + } + const issues = value.map((entry2, index) => { + const issue = record6(entry2, `Native check issue ${index}`); + exactKeys2(issue, ["path", "line", "kind"], `Native check issue ${index}`); + if (typeof issue.kind !== "string" || !ISSUE_KINDS.has(issue.kind)) { + throw new Error(`Native check issue ${index} kind is invalid`); + } + return { + path: projectRelativePath2(issue.path, `Native check issue ${index} path`), + line: positiveInteger5(issue.line, `Native check issue ${index} line`), + kind: issue.kind + }; + }); + const canonical = [...issues].sort(compareIssues); + if (JSON.stringify(canonical) !== JSON.stringify(issues)) { + throw new Error("Native check receipt issues must be canonical"); + } + return issues; +} +function parseStaleReasons(value) { + if (!Array.isArray(value) || value.some( + (entry2) => typeof entry2 !== "string" || !STALE_REASONS.has(entry2) + )) { + throw new Error("Native check receipt stale reasons are invalid"); + } + const reasons = value; + const canonical = STALE_REASON_ORDER.filter((reason) => reasons.includes(reason)); + if (new Set(reasons).size !== reasons.length || JSON.stringify(canonical) !== JSON.stringify(reasons)) { + throw new Error("Native check receipt stale reasons must be canonical"); + } + return [...reasons]; +} +function nativeCheckInputHash(value) { + return canonicalHash(CHECK_INPUT_HASH_TAG, value); +} +function parseNativeCheckReceipt(value) { + const receipt = record6(value, "Native check receipt"); + exactKeys2( + receipt, + [ + "schema", + "change", + "sourceRevision", + "checker", + "inputHash", + "status", + "startedAt", + "endedAt", + "contract", + "implementation", + "counts", + "issues", + "issuesTruncated", + "stale", + "staleReasons", + "receiptHash" + ], + "Native check receipt" + ); + if (receipt.schema !== NATIVE_CHECK_RECEIPT_SCHEMA) { + throw new Error("Native check receipt schema is invalid"); + } + if (typeof receipt.change !== "string" || Buffer.byteLength(receipt.change, "utf8") > 128 || !CHANGE_NAME_PATTERN3.test(receipt.change)) { + throw new Error("Native check receipt change name is invalid"); + } + const sourceRevision = positiveInteger5(receipt.sourceRevision, "Native check source revision"); + const checker = parseChecker(receipt.checker); + const contract = parseContract(receipt.contract); + const implementation = parseImplementation(receipt.implementation); + const expectedInputHash = nativeCheckInputHash({ + change: receipt.change, + sourceRevision, + checkerHash: checker.hash, + contractHash: contract.expectedHash, + scopeHash: implementation.scopeHash, + snapshotHash: implementation.expectedSnapshotHash + }); + if (hash5(receipt.inputHash, "Native check input hash") !== expectedInputHash) { + throw new Error("Native check receipt input hash mismatch"); + } + if (receipt.status !== "passed" && receipt.status !== "failed") { + throw new Error("Native check receipt status is invalid"); + } + const startedAt = isoTimestamp2(receipt.startedAt, "Native check receipt startedAt"); + const endedAt = isoTimestamp2(receipt.endedAt, "Native check receipt endedAt"); + if (endedAt < startedAt) throw new Error("Native check receipt endedAt precedes startedAt"); + const counts = parseCounts(receipt.counts); + const issues = parseIssues(receipt.issues); + if (counts.recordedIssueCount !== issues.length) { + throw new Error("Native check receipt recorded issue count is inconsistent"); + } + if (typeof receipt.issuesTruncated !== "boolean" || receipt.issuesTruncated !== counts.issueCount > issues.length) { + throw new Error("Native check receipt issue truncation flag is inconsistent"); + } + const staleReasons2 = parseStaleReasons(receipt.staleReasons); + if (typeof receipt.stale !== "boolean" || receipt.stale !== staleReasons2.length > 0) { + throw new Error("Native check receipt stale flag is inconsistent"); + } + const expectedStatus = counts.issueCount === 0 && !receipt.stale ? "passed" : "failed"; + if (receipt.status !== expectedStatus) { + throw new Error("Native check receipt status is inconsistent with its evidence"); + } + if (receipt.status === "passed" && (counts.filesSelected > NATIVE_CHECK_LIMITS.maxFiles || counts.filesScanned + counts.binaryFilesSkipped !== counts.filesSelected)) { + throw new Error("Passed Native check receipt must cover every selected file"); + } + if (counts.filesSelected > NATIVE_CHECK_LIMITS.maxFiles && !issues.some((issue) => issue.kind === "scan-limit")) { + throw new Error("Native check receipt exceeds its file budget without a scan-limit issue"); + } + const content = { + schema: NATIVE_CHECK_RECEIPT_SCHEMA, + change: receipt.change, + sourceRevision, + checker, + inputHash: expectedInputHash, + status: receipt.status, + startedAt, + endedAt, + contract, + implementation, + counts, + issues, + issuesTruncated: receipt.issuesTruncated, + stale: receipt.stale, + staleReasons: staleReasons2 + }; + const receiptHash = hash5(receipt.receiptHash, "Native check receipt content hash"); + if (canonicalHash(NATIVE_CHECK_RECEIPT_HASH_TAG, content) !== receiptHash) { + throw new Error("Native check receipt content hash mismatch"); + } + return { ...content, receiptHash }; +} +function buildNativeCheckReceipt(input) { + const checker = { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + hash: NATIVE_CHECKER_HASH, + limits: { ...NATIVE_CHECK_LIMITS } + }; + const content = { + schema: NATIVE_CHECK_RECEIPT_SCHEMA, + ...input, + checker, + inputHash: nativeCheckInputHash({ + change: input.change, + sourceRevision: input.sourceRevision, + checkerHash: checker.hash, + contractHash: input.contract.expectedHash, + scopeHash: input.implementation.scopeHash, + snapshotHash: input.implementation.expectedSnapshotHash + }) + }; + return parseNativeCheckReceipt({ + ...content, + receiptHash: canonicalHash(NATIVE_CHECK_RECEIPT_HASH_TAG, content) + }); +} + +// domains/comet-native/native-check-receipt-storage.ts +var HASH_PATTERN11 = /^[a-f0-9]{64}$/u; +var RECEIPT_REF_PATTERN = /^runtime\/evidence\/check-receipts\/([a-f0-9]{64})\.json$/u; +var MAX_NATIVE_CHECK_RECEIPT_BYTES = 512 * 1024; +function sameDirectoryIdentity6(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity7(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryChain5(root, directory) { + const lexicalRoot = path26.resolve(root); + const lexicalDirectory = path26.resolve(directory); + if (!isInsidePath(lexicalRoot, lexicalDirectory)) { + throw new Error("Native check receipt parent is outside the Native root"); + } + const chain = []; + let cursor = lexicalRoot; + for (const segment of [ + "", + ...path26.relative(lexicalRoot, lexicalDirectory).split(path26.sep).filter(Boolean) + ]) { + if (segment) cursor = path26.join(cursor, segment); + const stat = await fs17.lstat(cursor); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native check receipt parent must be a real directory: ${cursor}`); + } + const realPath = await fs17.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new Error("Native check receipt parent resolves outside the Native root"); + } + chain.push({ + path: cursor, + realPath, + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }); + } + return chain; +} +async function verifyDirectoryChain6(chain) { + for (const identity of chain) { + const stat = await fs17.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity6(identity, stat) || await fs17.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native check receipt parent changed while reading: ${identity.path}`); + } + } +} +function nativeCheckReceiptRef(hash6) { + if (!HASH_PATTERN11.test(hash6)) throw new Error("Native check receipt hash is invalid"); + return `runtime/evidence/check-receipts/${hash6}.json`; +} +function receiptHashFromRef(ref) { + const match = RECEIPT_REF_PATTERN.exec(ref); + if (!match) throw new Error("Native check receipt ref is invalid"); + return match[1]; +} +function receiptFile(paths, name, hash6) { + return path26.join(nativeChangeDir(paths, name), ...nativeCheckReceiptRef(hash6).split("/")); +} +async function readBoundedReceipt(file, changeRoot, nativeRoot) { + const chain = await captureDirectoryChain5(nativeRoot, path26.dirname(file)); + const before = await fs17.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error("Native check receipt must be a regular file"); + } + if (before.size > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + const [realChangeRoot, beforeRealPath] = await Promise.all([ + fs17.realpath(changeRoot), + fs17.realpath(file) + ]); + if (!isInsidePath(realChangeRoot, beforeRealPath)) { + throw new Error("Native check receipt resolves outside its change"); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants4.O_RDONLY | fsConstants4.O_NOFOLLOW | fsConstants4.O_NONBLOCK; + const handle = await fs17.open(file, openFlags).catch((error) => { + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new Error("Native check receipt became unsafe while opening"); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs17.lstat(file), + fs17.realpath(file) + ]); + await verifyDirectoryChain6(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity7(before, opened) || !sameFileIdentity7(opened, pathAfterOpen)) { + throw new Error("Native check receipt changed while opening"); + } + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(16 * 1024); + while (true) { + const remaining = MAX_NATIVE_CHECK_RECEIPT_BYTES + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs17.lstat(file), + fs17.realpath(file) + ]); + await verifyDirectoryChain6(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !isInsidePath(realChangeRoot, afterRealPath) || !sameFileIdentity7(opened, afterHandle) || !sameFileIdentity7(opened, afterPath)) { + throw new Error("Native check receipt changed while reading"); + } + return JSON.parse(Buffer.concat(chunks, total).toString("utf8")); + } finally { + await handle.close(); + } +} +async function readNativeCheckReceipt(paths, name, ref) { + const expectedHash = receiptHashFromRef(ref); + const changeRoot = nativeChangeDir(paths, name); + const file = receiptFile(paths, name, expectedHash); + await resolveContainedNativePath(paths.nativeRoot, file); + const receipt = parseNativeCheckReceipt( + await readBoundedReceipt(file, changeRoot, paths.nativeRoot) + ); + if (receipt.change !== name || receipt.receiptHash !== expectedHash) { + throw new Error("Native check receipt ref/hash/change mismatch"); + } + return receipt; +} +async function writeNativeCheckReceipt(options) { + const receipt = parseNativeCheckReceipt(options.receipt); + if (receipt.change !== options.name) { + throw new Error("Native check receipt change mismatch"); + } + const ref = nativeCheckReceiptRef(receipt.receiptHash); + const file = receiptFile(options.paths, options.name, receipt.receiptHash); + const serializedBytes = Buffer.byteLength(JSON.stringify(receipt, null, 2) + "\n", "utf8"); + if (serializedBytes > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + await resolveContainedNativePath(options.paths.nativeRoot, file); + try { + const existing = await readNativeCheckReceipt(options.paths, options.name, ref); + if (JSON.stringify(existing) !== JSON.stringify(receipt)) { + throw new Error(`Native check receipt hash collision for ${receipt.receiptHash}`); + } + return ref; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, receipt, { containedRoot: options.paths.nativeRoot }); + const persisted = await readNativeCheckReceipt(options.paths, options.name, ref); + if (JSON.stringify(persisted) !== JSON.stringify(receipt)) { + throw new Error("Native check receipt changed while being persisted"); + } + return ref; +} + +// domains/comet-native/native-contract.ts +import path27 from "node:path"; +var CONTRACT_HASH_TAG = "comet.native.contract.v1"; +var ACCEPTANCE_SET_HASH_TAG = "comet.native.acceptance-set.v1"; +var HASH_PATTERN12 = /^[a-f0-9]{64}$/u; +var NATIVE_CONTRACT_LIMITS = { + maxAcceptanceCriteria: NATIVE_ACCEPTANCE_LIMITS.maxCriteria +}; +function portableRef2(value, label) { + const normalized = path27.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path27.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized project-relative ref`); + } + return value; +} +function contentHash(value) { + return canonicalHash("comet.native.contract-content.v1", value.replace(/\r\n?/gu, "\n")); +} +function normalizeSpec3(input) { + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(input.capability)) { + throw new Error(`Invalid Native contract capability: ${input.capability}`); + } + if (input.operation !== "create" && input.operation !== "replace" && input.operation !== "remove") { + throw new Error(`Invalid Native contract operation for ${input.capability}`); + } + if (input.operation === "remove") { + if (input.source !== null || input.markdown !== null || !input.baseHash?.match(HASH_PATTERN12)) { + throw new Error(`Remove contract ${input.capability} requires only a base hash`); + } + return { + snapshot: { + capability: input.capability, + operation: "remove", + source: null, + baseHash: input.baseHash, + contentHash: null + }, + markdown: null + }; + } + if (input.source === null || input.markdown === null) { + throw new Error(`${input.operation} contract ${input.capability} requires source content`); + } + if (input.operation === "create" && input.baseHash !== null) { + throw new Error(`Create contract ${input.capability} requires a null base hash`); + } + if (input.operation === "replace" && !input.baseHash?.match(HASH_PATTERN12)) { + throw new Error(`Replace contract ${input.capability} requires a base hash`); + } + const source = portableRef2(input.source, `Contract source for ${input.capability}`); + return { + snapshot: { + capability: input.capability, + operation: input.operation, + source, + baseHash: input.baseHash, + contentHash: contentHash(input.markdown) + }, + markdown: input.markdown + }; +} +function compareCriteria(left, right) { + return compareText7(left.id, right.id); +} +function compareText7(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function buildNativeContractSnapshot(input) { + const briefSource = portableRef2(input.briefSource ?? "brief.md", "Native brief source"); + const specs = input.specs.map(normalizeSpec3).sort((left, right) => compareText7(left.snapshot.capability, right.snapshot.capability)); + const capabilities = specs.map(({ snapshot: snapshot2 }) => snapshot2.capability); + if (new Set(capabilities).size !== capabilities.length) { + throw new Error("Native contract contains duplicate capabilities"); + } + const sources = [ + briefSource, + ...specs.flatMap(({ snapshot: snapshot2 }) => snapshot2.source === null ? [] : [snapshot2.source]) + ]; + if (new Set(sources).size !== sources.length) { + throw new Error("Native contract contains duplicate artifact sources"); + } + const acceptance = deriveBriefAcceptanceCriteria( + input.briefMarkdown, + briefSource, + NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria + ); + for (const { snapshot: snapshot2, markdown } of specs) { + if (markdown === null || snapshot2.source === null) continue; + acceptance.push( + ...deriveSpecAcceptanceCriteria( + markdown, + snapshot2.source, + NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria - acceptance.length + ) + ); + } + acceptance.sort(compareCriteria); + if (acceptance.length === 0) { + throw new Error("Native contract has no structured acceptance criteria"); + } + if (acceptance.length > NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria) { + throw new Error( + `Native contract exceeds its ${NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria}-criterion acceptance budget` + ); + } + const acceptanceIds = acceptance.map(({ id }) => id); + if (new Set(acceptanceIds).size !== acceptanceIds.length) { + throw new Error("Native contract contains duplicate acceptance criteria"); + } + const brief = { source: briefSource, contentHash: contentHash(input.briefMarkdown) }; + const specSnapshots = specs.map(({ snapshot: snapshot2 }) => snapshot2); + const acceptanceHash = canonicalHash(ACCEPTANCE_SET_HASH_TAG, acceptance); + const contractContent = { + schema: "comet.native.contract.v1", + brief, + specs: specSnapshots, + acceptance, + acceptanceHash + }; + return { + ...contractContent, + contractHash: canonicalHash(CONTRACT_HASH_TAG, contractContent) + }; +} + +// domains/comet-native/native-contract-files.ts +var NATIVE_CONTRACT_FILE_LIMITS = { + maxSpecs: 64, + maxFileBytes: DEFAULT_NATIVE_ARTIFACT_MAX_BYTES, + maxTotalBytes: 4 * 1024 * 1024 +}; +async function collectNativeContractFiles(options) { + if (options.specChanges.length > NATIVE_CONTRACT_FILE_LIMITS.maxSpecs) { + throw new Error("Native contract exceeds its spec-count budget"); + } + const brief = await readNativeBoundedTextFile({ + root: options.changeDir, + ref: options.briefRef, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + let totalBytes = brief.size; + const specs = []; + for (const change of options.specChanges) { + if (change.operation === "remove") { + specs.push({ + capability: change.capability, + operation: "remove", + source: null, + baseHash: change.base_hash, + markdown: null + }); + continue; + } + if (!change.source) { + throw new Error(`Native contract ${change.capability} has no proposed spec source`); + } + const source = await readNativeBoundedTextFile({ + root: options.changeDir, + ref: change.source, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + totalBytes += source.size; + if (totalBytes > NATIVE_CONTRACT_FILE_LIMITS.maxTotalBytes) { + throw new Error("Native contract exceeds its total byte budget"); + } + specs.push({ + capability: change.capability, + operation: change.operation, + source: source.ref, + baseHash: change.base_hash, + markdown: source.text + }); + } + return { + contract: buildNativeContractSnapshot({ + briefSource: brief.ref, + briefMarkdown: brief.text, + specs + }), + sourceCount: specs.filter((spec) => spec.source !== null).length + 1, + totalBytes + }; +} + +// domains/comet-native/native-verification-runtime.ts +function projectionManifest(projection) { + return { + schema: "comet.native.content-snapshot.v1", + origin: projection.origin, + createdAt: "1970-01-01T00:00:00.000Z", + complete: projection.complete, + limits: projection.limits, + ...projection.policy ? { policy: projection.policy } : {}, + entries: projection.entries, + omitted: projection.omitted, + omittedCount: projection.omittedCount, + ...projection.omissionOverflow ? { omissionOverflow: projection.omissionOverflow } : {} + }; +} +function nativeRootRef2(paths) { + const value = path28.relative(paths.projectRoot, paths.nativeRoot).replaceAll("\\", "/"); + if (!value || value === ".." || value.startsWith("../") || path28.posix.isAbsolute(value)) { + throw new Error("Native root is outside the project root"); + } + return value; +} +async function currentProjectionHash(options) { + const baseline = projectionManifest(options.bundle.baseline); + const current = await createNativeCurrentContentSnapshot(options.paths, baseline, { + origin: "explicit", + now: options.now + }); + return buildNativeImplementationScopeBundle({ + baseline, + current, + contractHash: options.bundle.scope.contractHash, + declaredArtifacts: options.bundle.scope.declaredArtifacts, + noCodeReason: options.bundle.scope.noCodeReason + }).scope.currentProjectionHash; +} +async function inspectCurrentScopeFacts(options) { + if (!options.state.implementation_scope) { + throw new Error("Native change has no implementation scope"); + } + const bundle = await readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ); + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }); + const currentHash = await currentProjectionHash({ + paths: options.paths, + bundle, + now: options.now + }); + const findingCodes = []; + if (contract.contract.contractHash !== bundle.scope.contractHash) { + findingCodes.push("verification-contract-stale"); + } + if (currentHash !== bundle.scope.currentProjectionHash) { + findingCodes.push("verification-implementation-stale"); + } + return { + bundle, + contract: contract.contract, + contractHash: contract.contract.contractHash, + acceptanceHash: contract.contract.acceptanceHash, + findingCodes + }; +} +async function inspectNativeImplementationScopeFreshness(options) { + if (!options.state.implementation_scope) { + return { freshness: "missing", findingCodes: ["verification-evidence-missing"] }; + } + try { + const facts = await inspectCurrentScopeFacts(options); + const findingCodes = [...new Set(facts.findingCodes)].sort(); + return { + freshness: findingCodes.length === 0 ? "fresh" : "stale", + findingCodes + }; + } catch { + return { freshness: "invalid", findingCodes: ["verification-evidence-invalid"] }; + } +} +async function reportEvidence(options) { + const report = await readNativeBoundedTextFile({ + root: nativeChangeDir(options.paths, options.state.name), + ref: options.reportRef + }); + return { + ref: report.ref, + hash: report.hash, + text: report.text, + entries: parseNativeVerificationMachineBlock(report.text) + }; +} +function checkReceiptBindingCodes(options) { + const { receipt, implementationScope } = options; + const codes = []; + const selectedFiles = implementationScope.scope.changes.filter((change) => change.after !== null); + const selectedBytes = selectedFiles.reduce((total, change) => total + change.after.size, 0); + if (receipt.stale || receipt.sourceRevision !== options.sourceRevision || receipt.contract.expectedHash !== options.contractHash || receipt.contract.beforeHash !== options.contractHash || receipt.contract.afterHash !== options.contractHash || receipt.implementation.scopeHash !== implementationScope.scope.scopeHash || receipt.implementation.expectedSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.implementation.beforeSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.implementation.afterSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.counts.filesSelected !== selectedFiles.length || receipt.status === "passed" && (receipt.counts.filesScanned + receipt.counts.binaryFilesSkipped !== selectedFiles.length || receipt.counts.bytesScanned !== selectedBytes)) { + codes.push("verification-receipt-stale"); + } + if (options.result === "pass" && receipt.status !== "passed") { + codes.push("verification-receipt-outcome-mismatch"); + } + return codes; +} +async function inspectNativeVerificationEvidence(options) { + if (options.state.phase !== "verify") { + throw new Error(`Native verification evidence requires Verify, got ${options.state.phase}`); + } + const facts = await inspectCurrentScopeFacts(options); + if (facts.findingCodes.length > 0) { + return { + ready: false, + findingCodes: facts.findingCodes, + envelope: null, + evidenceRef: null, + reportSnapshot: null + }; + } + const report = await reportEvidence(options); + let receiptRef = null; + if (options.receiptRef) { + const receipt = await readNativeCheckReceipt( + options.paths, + options.state.name, + options.receiptRef + ); + const receiptCodes = checkReceiptBindingCodes({ + receipt, + sourceRevision: options.state.revision, + result: options.result, + contractHash: facts.contractHash, + implementationScope: facts.bundle + }); + if (receiptCodes.length > 0) { + throw new Error(`Native verification receipt is not admissible: ${receiptCodes.join(", ")}`); + } + receiptRef = options.receiptRef; + } + const trace = buildNativeAcceptanceEvidenceTrace(facts.contract.acceptance, report.entries, { + nativeRootRef: nativeRootRef2(options.paths) + }); + const allowance = options.state.partial_allowance ? await readNativePartialAllowance( + options.paths, + options.state.name, + options.state.partial_allowance + ) : null; + const envelope = buildNativeVerificationEvidenceEnvelope({ + change: options.state.name, + sourceRevision: options.state.revision, + result: options.result, + contractHash: facts.contractHash, + acceptanceHash: facts.acceptanceHash, + implementationScope: { + ref: options.state.implementation_scope, + bundle: facts.bundle + }, + reportRef: report.ref, + reportHash: report.hash, + receiptRef, + acceptanceTrace: trace, + partialAllowance: options.state.partial_allowance && allowance ? { ref: options.state.partial_allowance, allowance } : null, + now: options.now + }); + const evidenceRef = nativeEvidenceRef("verifications", envelope.envelopeHash); + return { + ready: true, + findingCodes: [], + envelope, + evidenceRef, + reportSnapshot: { hash: report.hash, text: report.text } + }; +} +async function persistNativeVerificationEvidence(options) { + if (!options.preparation.ready || options.preparation.envelope === null || options.preparation.evidenceRef === null || options.preparation.reportSnapshot === null) { + throw new Error("Native verification evidence is not ready to persist"); + } + await writeNativeVerificationReportSnapshot({ + paths: options.paths, + name: options.state.name, + ...options.preparation.reportSnapshot + }); + const evidenceRef = await writeNativeVerificationEvidence({ + paths: options.paths, + name: options.state.name, + evidence: options.preparation.envelope + }); + if (evidenceRef !== options.preparation.evidenceRef) { + throw new Error("Native verification evidence persistence ref changed"); + } +} +function emptyEvidence(result2, freshness) { + return { + result: result2, + freshness, + contractHash: null, + acceptanceHash: null, + implementationScopeHash: null, + reportHash: null, + envelopeHash: null, + partialAllowanceHash: null, + skippedAcceptanceCount: 0 + }; +} +async function inspectNativeVerificationFreshness(options) { + if (!options.state.implementation_scope || !options.state.verification_evidence || !options.state.verification_report) { + return { + freshness: "missing", + findingCodes: ["verification-evidence-missing"], + evidence: emptyEvidence(options.state.verification_result, "missing"), + envelope: null + }; + } + try { + const [facts, envelope, report] = await Promise.all([ + inspectCurrentScopeFacts(options), + readNativeVerificationEvidence( + options.paths, + options.state.name, + options.state.verification_evidence + ), + reportEvidence({ + paths: options.paths, + state: options.state, + reportRef: options.state.verification_report + }) + ]); + const findingCodes = [...facts.findingCodes]; + if (report.hash !== envelope.reportHash || report.ref !== envelope.reportRef) { + findingCodes.push("verification-report-stale"); + } + if (envelope.result !== options.state.verification_result || envelope.implementationScopeRef !== options.state.implementation_scope || envelope.partialAllowanceRef !== options.state.partial_allowance || envelope.sourceRevision >= options.state.revision || envelope.contractHash !== facts.bundle.scope.contractHash || envelope.acceptanceCriteriaHash !== facts.acceptanceHash) { + findingCodes.push("verification-state-mismatch"); + } + if (envelope.receiptRef) { + try { + const receipt = await readNativeCheckReceipt( + options.paths, + options.state.name, + envelope.receiptRef + ); + findingCodes.push( + ...checkReceiptBindingCodes({ + receipt, + sourceRevision: envelope.sourceRevision, + result: envelope.result, + contractHash: envelope.contractHash, + implementationScope: facts.bundle + }) + ); + } catch { + findingCodes.push("verification-receipt-invalid"); + } + } + const uniqueCodes = [...new Set(findingCodes)].sort(); + const freshness = uniqueCodes.length > 0 ? "stale" : envelope.freshness; + return { + freshness, + findingCodes: uniqueCodes, + evidence: { + result: options.state.verification_result, + freshness, + contractHash: envelope.contractHash, + acceptanceHash: envelope.acceptanceCriteriaHash, + implementationScopeHash: envelope.implementationScopeHash, + reportHash: envelope.reportHash, + envelopeHash: envelope.envelopeHash, + partialAllowanceHash: envelope.partialAllowanceHash, + skippedAcceptanceCount: envelope.acceptanceTrace.skipped + }, + envelope + }; + } catch { + return { + freshness: "invalid", + findingCodes: ["verification-evidence-invalid"], + evidence: emptyEvidence(options.state.verification_result, "invalid"), + envelope: null + }; + } +} + +// domains/comet-native/native-archive-inspection.ts +function archiveTargetRef(name, now) { + return `archive/${now.toISOString().slice(0, 10)}-${name}`; +} +async function exists2(target) { + try { + await fs18.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function optionalBoundedHash(root, ref) { + try { + return (await readNativeBoundedTextFile({ root, ref })).hash; + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function specFact(paths, name, change) { + const canonical = canonicalSpecPath(paths, change.capability); + if (!isInsidePath(paths.nativeRoot, canonical)) { + throw new Error(`Native canonical spec escapes its root: ${change.capability}`); + } + const canonicalRef = path29.relative(paths.nativeRoot, canonical).replaceAll("\\", "/"); + const actualBaseHash = await optionalBoundedHash(paths.nativeRoot, canonicalRef); + let proposedHash = null; + if (change.operation !== "remove") { + if (!change.source) throw new Error(`Native proposed spec is missing: ${change.capability}`); + proposedHash = (await readNativeBoundedTextFile({ + root: nativeChangeDir(paths, name), + ref: change.source + })).hash; + } + return { + capability: change.capability, + operation: change.operation, + expectedBaseHash: change.operation === "create" ? null : change.base_hash, + actualBaseHash, + proposedHash + }; +} +async function hasPendingTransition(paths, name) { + return exists2(nativeTransitionJournalFile(paths, name)); +} +async function inspectNativeArchivePreflight(options) { + const now = options.now ?? /* @__PURE__ */ new Date(); + const state = await readNativeChange(options.paths, options.name); + const targetRef = archiveTargetRef(state.name, now); + const target = path29.resolve(options.paths.nativeRoot, ...targetRef.split("/")); + if (!isInsidePath(options.paths.nativeRoot, target)) { + throw new Error("Native archive target escapes its root"); + } + const [ + specs, + evidence, + conflicts, + pendingSchema, + pendingCheckpoint, + pendingTransition, + targetExists + ] = await Promise.all([ + Promise.all(state.spec_changes.map((change) => specFact(options.paths, state.name, change))), + inspectNativeVerificationFreshness({ paths: options.paths, state, now }), + inspectNativeChangeConflicts(options.paths, state.name), + hasPendingNativeSchemaMigration(options.paths, state.name), + hasPendingNativeCheckpointRecovery(options.paths, state.name), + hasPendingTransition(options.paths, state.name), + exists2(target) + ]); + return buildNativeArchivePreflight({ + change: state.name, + stateSchema: state.schema, + revision: state.revision, + phase: state.phase, + archived: state.archived, + pendingJournal: pendingSchema || pendingCheckpoint || pendingTransition, + targetRef, + targetExists, + specs, + evidence: evidence.evidence, + findingCodes: [...evidence.findingCodes, ...conflicts.findingCodes] + }); +} + +// domains/comet-native/native-archive-content.ts +import { promises as fs19 } from "node:fs"; +import path30 from "node:path"; +var TREE_HASH_TAG = "comet.native.archive-tree.v1"; +var NATIVE_ARCHIVE_CONTENT_LIMITS = { + maxDepth: 128, + maxEntries: 2e4, + maxFileBytes: 64 * 1024 * 1024, + maxTotalBytes: 256 * 1024 * 1024, + maxManifestBytes: 16 * 1024 * 1024, + maxRefBytes: 4 * 1024 +}; +function normalizedLimits(limits) { + const resolved = { ...NATIVE_ARCHIVE_CONTENT_LIMITS, ...limits }; + for (const [name, value] of Object.entries(resolved)) { + if (!Number.isSafeInteger(value) || value <= 0) { + throw new Error(`Native Archive content limit ${name} must be a positive safe integer`); + } + } + return resolved; +} +function directorySnapshot(entries) { + return JSON.stringify( + entries.map((entry2) => ({ + name: entry2.name, + kind: entry2.isDirectory() ? "directory" : entry2.isFile() ? "file" : entry2.isSymbolicLink() ? "symlink" : "other" + })).sort((left, right) => left.name.localeCompare(right.name)) + ); +} +function appendTreeEntry(entries, entry2, budget, limits) { + budget.entryCount += 1; + if (budget.entryCount > limits.maxEntries) { + throw new Error(`Native Archive content exceeds ${limits.maxEntries} entries`); + } + if (Buffer.byteLength(entry2.ref, "utf8") > limits.maxRefBytes) { + throw new Error(`Native Archive content ref exceeds ${limits.maxRefBytes} bytes: ${entry2.ref}`); + } + const entryBytes = Buffer.byteLength(JSON.stringify(entry2), "utf8"); + budget.manifestBytes += entryBytes + (entries.length === 0 ? 0 : 1); + if (budget.manifestBytes + 2 > limits.maxManifestBytes) { + throw new Error(`Native Archive content manifest exceeds ${limits.maxManifestBytes} bytes`); + } + entries.push(entry2); +} +async function walkArchiveTree(root, directory, entries, budget, limits, depth) { + if (depth > limits.maxDepth) { + throw new Error(`Native Archive content exceeds depth ${limits.maxDepth}`); + } + const protectedDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: `Native Archive content directory ${path30.relative(root, directory) || "."}`, + maxEntries: limits.maxEntries + }); + const beforeSnapshot = directorySnapshot(protectedDirectory.entries); + const children = [...protectedDirectory.entries]; + children.sort((left, right) => left.name.localeCompare(right.name)); + for (const child of children) { + await protectedDirectory.verify(); + const target = path30.join(directory, child.name); + const ref = path30.relative(root, target).replaceAll("\\", "/"); + const stat = await fs19.lstat(target); + if (child.isSymbolicLink() || stat.isSymbolicLink()) { + throw new Error(`Native Archive content must not contain symlinks or junctions: ${ref}`); + } + if (child.isDirectory() && stat.isDirectory()) { + appendTreeEntry(entries, { ref, kind: "directory" }, budget, limits); + await walkArchiveTree(root, target, entries, budget, limits, depth + 1); + continue; + } + if (!child.isFile() || !stat.isFile()) { + throw new Error(`Native Archive content must contain only files and directories: ${ref}`); + } + const snapshot2 = await readNativeProtectedFile({ + root, + file: target, + maxBytes: limits.maxFileBytes, + label: `Native Archive content file ${ref}` + }); + budget.totalBytes += snapshot2.size; + if (budget.totalBytes > limits.maxTotalBytes) { + throw new Error(`Native Archive content exceeds ${limits.maxTotalBytes} total file bytes`); + } + appendTreeEntry( + entries, + { ref, kind: "file", hash: snapshot2.hash, size: snapshot2.size }, + budget, + limits + ); + await protectedDirectory.verify(); + } + await protectedDirectory.verify(); + const afterDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: `Native Archive content directory ${path30.relative(root, directory) || "."}`, + maxEntries: limits.maxEntries + }); + if (directorySnapshot(afterDirectory.entries) !== beforeSnapshot) { + throw new Error( + `Native Archive content directory changed while reading: ${path30.relative(root, directory) || "."}` + ); + } + await Promise.all([protectedDirectory.verify(), afterDirectory.verify()]); +} +async function hashNativeArchiveTree(directory, requestedLimits = {}) { + const limits = normalizedLimits(requestedLimits); + directory = path30.resolve(directory); + const stat = await fs19.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native Archive move source must be a real directory: ${directory}`); + } + const entries = []; + const budget = { entryCount: 0, totalBytes: 0, manifestBytes: 0 }; + await walkArchiveTree(directory, directory, entries, budget, limits, 0); + const manifest = JSON.stringify(entries); + if (Buffer.byteLength(manifest, "utf8") > limits.maxManifestBytes) { + throw new Error(`Native Archive content manifest exceeds ${limits.maxManifestBytes} bytes`); + } + return sha256Text(`${TREE_HASH_TAG}\0${manifest}`); +} +async function inspectNativeArchiveContent(target, requestedLimits = {}) { + const limits = normalizedLimits(requestedLimits); + target = path30.resolve(target); + let stat; + try { + stat = await fs19.lstat(target); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (stat.isSymbolicLink()) { + throw new Error(`Native Archive transaction path must not be a symlink or junction: ${target}`); + } + if (stat.isFile()) { + const snapshot2 = await readNativeProtectedFile({ + root: path30.dirname(target), + file: target, + maxBytes: limits.maxFileBytes, + label: `Native Archive transaction file ${path30.basename(target)}` + }); + return { kind: "file", hash: snapshot2.hash }; + } + if (stat.isDirectory()) { + return { kind: "directory", hash: await hashNativeArchiveTree(target, limits) }; + } + throw new Error(`Native Archive transaction path has an unsupported file type: ${target}`); +} + +// domains/comet-native/native-archive-transaction.ts +import { promises as fs20 } from "node:fs"; +import path31 from "node:path"; +var NATIVE_ARCHIVE_COPY_MAX_BYTES = 16 * 1024 * 1024; +var NATIVE_ARCHIVE_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_ARCHIVE_CAS_RECORD_MAX_BYTES = 16 * 1024; +var NATIVE_ARCHIVE_CAS_SCHEMA = "comet.native.archive-cas.v1"; +function resolveRefLexically2(paths, ref) { + const target = path31.resolve(paths.nativeRoot, ...ref.split("/")); + if (path31.relative(paths.nativeRoot, target).split(path31.sep).includes("..")) { + throw new Error(`Unsafe Native Archive transaction ref: ${ref}`); + } + return target; +} +async function resolveRef2(paths, ref) { + return resolveContainedNativePath(paths.nativeRoot, resolveRefLexically2(paths, ref)); +} +function sameContent(actual, expectedHash, expectedKind = "file") { + return expectedHash === null ? actual === null : actual?.kind === expectedKind && actual.hash === expectedHash; +} +function contentDescription(value) { + return value === null ? "missing" : `${value.kind}:${value.hash}`; +} +async function assertContent(options) { + const actual = await inspectNativeArchiveContent(options.target); + if (!sameContent(actual, options.expectedHash, options.expectedKind)) { + throw new Error( + `${options.label} content changed: expected ${options.expectedHash ?? "missing"}, got ${contentDescription(actual)}` + ); + } + return actual; +} +async function createNativeArchiveTransactionV2(paths, journal) { + const validated = parseNativeArchiveTransactionJournalV2(journal); + const tx = await resolveNativeTransactionPaths(paths, validated.id); + await fs20.mkdir(tx.staged, { recursive: true }); + await fs20.mkdir(tx.backups, { recursive: true }); + await atomicWriteJson(tx.journal, validated, { containedRoot: paths.nativeRoot }); + await appendNativeTransactionEvent(paths, validated.id, "prepared"); +} +async function readNativeArchiveTransactionV2(paths, id) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.journal, + maxBytes: NATIVE_ARCHIVE_JOURNAL_MAX_BYTES, + label: `Native Archive transaction journal ${id}` + }); + const journal = parseNativeArchiveTransactionJournalV2( + JSON.parse(snapshot2.bytes.toString("utf8")) + ); + if (journal.id !== id) throw new Error(`Invalid Native Archive transaction journal: ${id}`); + return journal; +} +async function setStatus(paths, journal, status) { + const updated = parseNativeArchiveTransactionJournalV2({ ...journal, status }); + const tx = await resolveNativeTransactionPaths(paths, updated.id); + await atomicWriteJson(tx.journal, updated, { containedRoot: paths.nativeRoot }); + return updated; +} +function fileObjectIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +function fileVersion(stat) { + return { + ...fileObjectIdentity(stat), + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileObject2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs; +} +function sameFileVersion(expected, actual) { + return sameFileObject2(expected, actual) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function pathExists(target) { + try { + await fs20.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function captureStableArchiveFile(options) { + const before = await fs20.lstat(options.file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + const beforeVersion = fileVersion(before); + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: options.file, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: options.label + }); + const after = await fs20.lstat(options.file); + if (!after.isFile() || after.isSymbolicLink() || !sameFileVersion(beforeVersion, fileVersion(after)) || snapshot2.hash !== options.expectedHash) { + throw new Error(`${options.label} content changed or object identity changed while binding`); + } + return { identity: fileObjectIdentity(after), size: snapshot2.size }; +} +function parseCasRecord(value, operation, role, expectedHash) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Archive CAS ${operation.id} ${role} record must be an object`); + } + const record8 = value; + const keys = Object.keys(record8).sort(); + if (keys.join(",") !== "hash,identity,operationId,role,schema") { + throw new Error(`Archive CAS ${operation.id} ${role} record has an invalid shape`); + } + const identity = record8.identity; + if (!identity || typeof identity !== "object" || Array.isArray(identity)) { + throw new Error(`Archive CAS ${operation.id} ${role} identity must be an object`); + } + const identityRecord = identity; + if (Object.keys(identityRecord).sort().join(",") !== "birthtimeMs,dev,ino") { + throw new Error(`Archive CAS ${operation.id} ${role} identity has an invalid shape`); + } + for (const field2 of ["dev", "ino", "birthtimeMs"]) { + if (typeof identityRecord[field2] !== "number" || !Number.isFinite(identityRecord[field2]) || identityRecord[field2] < 0) { + throw new Error(`Archive CAS ${operation.id} ${role} identity ${field2} is invalid`); + } + } + if (record8.schema !== NATIVE_ARCHIVE_CAS_SCHEMA || record8.operationId !== operation.id || record8.role !== role || record8.hash !== expectedHash) { + throw new Error(`Archive CAS ${operation.id} ${role} record is not transaction-bound`); + } + return { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: operation.id, + role, + hash: expectedHash, + identity: { + dev: identityRecord.dev, + ino: identityRecord.ino, + birthtimeMs: identityRecord.birthtimeMs + } + }; +} +async function archiveCasPaths(paths, journal, operation, target) { + const tx = await resolveNativeTransactionPaths(paths, journal.id); + const directory = path31.join(tx.backups, ".cas"); + const prefix = `.${path31.basename(target)}.comet-archive-cas-${journal.id}-${operation.id}`; + const result2 = { + directory, + originalRecord: path31.join(directory, `${operation.id}.original.json`), + postRecord: path31.join(directory, `${operation.id}.post.json`), + candidate: path31.join(directory, `${operation.id}.candidate`), + originalQuarantine: path31.join(path31.dirname(target), `${prefix}.original`), + rollbackQuarantine: path31.join(path31.dirname(target), `${prefix}.rollback`) + }; + await Promise.all( + Object.values(result2).map((item) => resolveContainedNativePath(paths.nativeRoot, item)) + ); + return result2; +} +async function readCasRecord(options) { + if (!await pathExists(options.file)) return null; + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: options.file, + maxBytes: NATIVE_ARCHIVE_CAS_RECORD_MAX_BYTES, + label: `Archive CAS ${options.operation.id} ${options.role} record` + }); + return parseCasRecord( + JSON.parse(snapshot2.bytes.toString("utf8")), + options.operation, + options.role, + options.expectedHash + ); +} +async function persistCasRecord(options) { + const existing = await readCasRecord(options); + if (existing) { + if (!sameFileObject2(existing.identity, options.identity)) { + throw new Error( + `Archive CAS ${options.operation.id} ${options.role} object identity changed` + ); + } + return existing; + } + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.file), + label: `Archive CAS ${options.operation.id} records` + }); + const record8 = { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: options.role, + hash: options.expectedHash, + identity: options.identity + }; + try { + await atomicWriteJson(options.file, record8, { + containedRoot: options.paths.nativeRoot, + exclusive: true + }); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const persisted = await readCasRecord(options); + if (!persisted || !sameFileObject2(persisted.identity, options.identity)) { + throw new Error(`Archive CAS ${options.operation.id} ${options.role} record changed`); + } + return persisted; +} +async function validateFileAgainstCasRecord(options) { + const current = await captureStableArchiveFile({ + paths: options.paths, + file: options.file, + expectedHash: options.record.hash, + label: options.label + }); + if (!sameFileObject2(options.record.identity, current.identity)) { + throw new Error(`${options.label} object identity changed`); + } +} +async function bindCurrentTarget(options) { + const current = await captureStableArchiveFile({ + paths: options.paths, + file: options.file, + expectedHash: options.expectedHash, + label: options.label + }); + const record8 = await persistCasRecord({ + paths: options.paths, + file: options.recordFile, + operation: options.operation, + role: options.role, + expectedHash: options.expectedHash, + identity: current.identity + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.file, + operation: options.operation, + record: record8, + label: options.label + }); + return record8; +} +async function restoreUnexpectedQuarantine(options) { + if (!await pathExists(options.quarantine) || await pathExists(options.target)) return; + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive CAS ${options.operation.id} quarantine restoration` + }); + await guard.verify(); + try { + await fs20.link(options.quarantine, options.target); + } catch (error) { + if (error.code === "EEXIST") return; + throw error; + } + await guard.verify(); + const [quarantine, target] = await Promise.all([ + fs20.lstat(options.quarantine), + fs20.lstat(options.target) + ]); + if (!sameFileObject2(fileObjectIdentity(quarantine), fileObjectIdentity(target))) { + throw new Error(`Archive CAS ${options.operation.id} could not restore quarantined content`); + } + await fs20.unlink(options.quarantine); + await guard.verify(); +} +async function quarantineBoundTarget(options) { + if (await pathExists(options.quarantine)) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} quarantine ${options.operation.target}` + }); + return; + } + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive ${options.phase} target ${options.operation.target}` + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} target ${options.operation.target}` + }); + await options.hooks?.afterArchiveTargetBound?.(options.phase, options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} target ${options.operation.target}` + }); + try { + await fs20.rename(options.target, options.quarantine); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} quarantine ${options.operation.target}` + }); + } catch (error) { + await restoreUnexpectedQuarantine(options); + throw new Error( + `Archive ${options.phase} target ${options.operation.target} changed during quarantine`, + { cause: error } + ); + } + await options.hooks?.afterArchiveTargetQuarantined?.( + options.phase, + options.operation, + options.quarantine + ); +} +async function removeExactCasFile(options) { + if (!await pathExists(options.file)) return; + await validateFileAgainstCasRecord(options); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.file), + label: options.label + }); + await guard.verify(); + await validateFileAgainstCasRecord(options); + await fs20.unlink(options.file); + await guard.verify(); +} +async function ensureBackup(paths, operation, hooks) { + if (!operation.backup || operation.expectedTargetHash === null) return; + const target = await resolveRef2(paths, operation.target); + const backup = await resolveRef2(paths, operation.backup); + const existing = await inspectNativeArchiveContent(backup); + if (existing !== null) { + if (!sameContent(existing, operation.expectedTargetHash)) { + throw new Error(`Archive transaction backup content changed: ${operation.backup}`); + } + return; + } + await assertContent({ + target, + expectedHash: operation.expectedTargetHash, + label: `Archive transaction target ${operation.target}` + }); + await copyNativeProtectedFile({ + sourceRoot: paths.nativeRoot, + source: target, + targetRoot: paths.nativeRoot, + target: backup, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: `Archive transaction backup ${operation.backup}`, + expectedHash: operation.expectedTargetHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => hooks?.afterProtectedCopySourceParentCaptured?.("backup", operation.target) + } + }); + await assertContent({ + target: backup, + expectedHash: operation.expectedTargetHash, + label: `Archive transaction backup ${operation.backup}` + }); +} +async function ensureOriginalTargetQuarantined(options) { + const expectedHash = options.operation.expectedTargetHash; + if (expectedHash === null) { + throw new Error( + `Archive operation ${options.operation.id} has no original target to quarantine` + ); + } + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + let record8 = await readCasRecord({ + paths: options.paths, + file: cas.originalRecord, + operation: options.operation, + role: "original", + expectedHash + }); + if (await pathExists(cas.originalQuarantine)) { + if (!record8) { + throw new Error( + `Archive apply quarantine ${options.operation.target} has no bound object identity` + ); + } + await validateFileAgainstCasRecord({ + paths: options.paths, + file: cas.originalQuarantine, + operation: options.operation, + record: record8, + label: `Archive apply quarantine ${options.operation.target}` + }); + await ensureBackup(options.paths, options.operation, options.hooks); + return { record: record8, quarantine: cas.originalQuarantine }; + } + if (!await pathExists(options.target)) { + throw new Error( + `Archive apply target ${options.operation.target} disappeared before quarantine` + ); + } + record8 ??= await bindCurrentTarget({ + paths: options.paths, + file: options.target, + recordFile: cas.originalRecord, + operation: options.operation, + role: "original", + expectedHash, + label: `Archive apply target ${options.operation.target}` + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: record8, + label: `Archive apply target ${options.operation.target}` + }); + await ensureBackup(options.paths, options.operation, options.hooks); + await quarantineBoundTarget({ + paths: options.paths, + operation: options.operation, + phase: "apply", + target: options.target, + quarantine: cas.originalQuarantine, + record: record8, + hooks: options.hooks + }); + return { record: record8, quarantine: cas.originalQuarantine }; +} +async function ensureWriteCandidate(options) { + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + if (await pathExists(cas.candidate)) { + await captureStableArchiveFile({ + paths: options.paths, + file: cas.candidate, + expectedHash: options.operation.stagedHash, + label: `Archive write candidate ${options.operation.target}` + }); + return cas.candidate; + } + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: cas.directory, + label: `Archive CAS ${options.operation.id} records` + }); + await copyNativeProtectedFile({ + sourceRoot: options.paths.nativeRoot, + source: options.staged, + targetRoot: options.paths.nativeRoot, + target: cas.candidate, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: `Archive transaction staged file ${options.operation.staged}`, + expectedHash: options.operation.stagedHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => options.hooks?.afterProtectedCopySourceParentCaptured?.("apply", options.operation.staged) + } + }); + return cas.candidate; +} +async function ensureWriteInstalled(options) { + const expectedHash = options.operation.stagedHash; + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + let record8 = await readCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash + }); + if (record8 && await pathExists(options.target)) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: record8, + label: `Archive write target ${options.operation.target}` + }); + if (await pathExists(cas.candidate)) { + await removeExactCasFile({ + paths: options.paths, + file: cas.candidate, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + } + return record8; + } + if (await pathExists(options.target)) { + const candidate2 = await ensureWriteCandidate(options); + const candidateIdentity2 = await captureStableArchiveFile({ + paths: options.paths, + file: candidate2, + expectedHash, + label: `Archive write candidate ${options.operation.target}` + }); + const targetIdentity2 = await captureStableArchiveFile({ + paths: options.paths, + file: options.target, + expectedHash, + label: `Archive write target ${options.operation.target}` + }); + if (!sameFileObject2(candidateIdentity2.identity, targetIdentity2.identity)) { + throw new Error( + `Archive write target ${options.operation.target} is occupied by an external object` + ); + } + record8 = await persistCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash, + identity: targetIdentity2.identity + }); + await removeExactCasFile({ + paths: options.paths, + file: candidate2, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + return record8; + } + const candidate = await ensureWriteCandidate(options); + const candidateIdentity = await captureStableArchiveFile({ + paths: options.paths, + file: candidate, + expectedHash, + label: `Archive write candidate ${options.operation.target}` + }); + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive write target ${options.operation.target}` + }); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive write target ${options.operation.target}` + }); + await options.hooks?.beforeArchiveTargetInstall?.("apply", options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: candidate, + operation: options.operation, + record: { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: "post", + hash: expectedHash, + identity: candidateIdentity.identity + }, + label: `Archive write candidate ${options.operation.target}` + }); + try { + await fs20.link(candidate, options.target); + } catch (error) { + if (error.code === "EEXIST") { + throw new Error( + `Archive write target ${options.operation.target} was created before exclusive install`, + { cause: error } + ); + } + throw error; + } + await guard.verify(); + await options.hooks?.afterArchiveTargetInstalled?.("apply", options.operation, options.target); + const targetIdentity = await captureStableArchiveFile({ + paths: options.paths, + file: options.target, + expectedHash, + label: `Archive write target ${options.operation.target}` + }); + if (!sameFileObject2(candidateIdentity.identity, targetIdentity.identity)) { + throw new Error(`Archive write target ${options.operation.target} changed during install`); + } + record8 = await persistCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash, + identity: targetIdentity.identity + }); + await removeExactCasFile({ + paths: options.paths, + file: candidate, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + return record8; +} +async function applyWrite(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + const staged = await resolveRef2(paths, operation.staged); + await assertContent({ + target: staged, + expectedHash: operation.stagedHash, + label: `Archive transaction staged file ${operation.staged}` + }); + if (operation.expectedTargetHash !== null) { + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord || !await pathExists(target)) { + await ensureOriginalTargetQuarantined({ paths, journal, operation, target, hooks }); + } + } else { + const actual = await inspectNativeArchiveContent(target); + if (actual !== null && !sameContent(actual, operation.stagedHash)) { + throw new Error( + `Archive transaction target ${operation.target} changed before create: ${contentDescription(actual)}` + ); + } + } + await ensureWriteInstalled({ + paths, + journal, + operation, + staged, + target, + hooks + }); + await assertContent({ + target, + expectedHash: operation.stagedHash, + label: `Archive transaction target ${operation.target}` + }); +} +async function applyRemove(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + await ensureOriginalTargetQuarantined({ paths, journal, operation, target, hooks }); + await assertContent({ + target, + expectedHash: null, + label: `Archive transaction target ${operation.target}` + }); +} +async function applyMove(paths, operation) { + const source = await resolveRef2(paths, operation.source); + const target = await resolveRef2(paths, operation.target); + const [sourceContent, targetContent] = await Promise.all([ + inspectNativeArchiveContent(source), + inspectNativeArchiveContent(target) + ]); + if (sourceContent === null && sameContent(targetContent, operation.expectedSourceHash, "directory")) { + return; + } + if (!sameContent(sourceContent, operation.expectedSourceHash, "directory") || targetContent !== null) { + throw new Error( + `Archive transaction move ${operation.id} content changed: source=${contentDescription(sourceContent)}, target=${contentDescription(targetContent)}` + ); + } + await fs20.mkdir(path31.dirname(target), { recursive: true }); + await fs20.rename(source, target); + await assertContent({ + target, + expectedHash: operation.expectedSourceHash, + expectedKind: "directory", + label: `Archive transaction move target ${operation.target}` + }); +} +async function applyOperation2(paths, journal, operation, hooks) { + if (operation.type === "write") return applyWrite(paths, journal, operation, hooks); + if (operation.type === "remove") return applyRemove(paths, journal, operation, hooks); + return applyMove(paths, operation); +} +function completedOperationIds(options) { + let operationIndex = 0; + let startedCurrent = false; + const completed = []; + for (const event of options.events) { + if (event.type !== "operation-started" && event.type !== "operation-completed") continue; + const expected = options.journal.operations[operationIndex]; + if (!expected || event.operationId !== expected.id) { + throw new Error( + `Native Archive transaction ${options.journal.id} operation events are out of order` + ); + } + if (event.type === "operation-started") { + startedCurrent = true; + continue; + } + if (!startedCurrent) { + throw new Error( + `Native Archive transaction ${options.journal.id} completed an operation before it started` + ); + } + completed.push(expected.id); + operationIndex += 1; + startedCurrent = false; + } + return completed; +} +async function assertCompletedOperation(paths, journal, operation, finalizationStarted) { + const target = await resolveRef2(paths, operation.target); + if (operation.type === "write") { + const staged = await resolveRef2(paths, operation.staged); + await assertContent({ + target: staged, + expectedHash: operation.stagedHash, + label: `Completed Archive staged file ${operation.staged}` + }); + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord) { + throw new Error(`Completed Archive target ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: target, + operation, + record: postRecord, + label: `Completed Archive target ${operation.target}` + }); + if (operation.expectedTargetHash !== null) { + await assertContent({ + target: await resolveRef2(paths, operation.backup), + expectedHash: operation.expectedTargetHash, + label: `Completed Archive backup ${operation.backup}` + }); + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error( + `Completed Archive target ${operation.target} has no original CAS identity` + ); + } + if (await pathExists(cas.originalQuarantine)) { + await validateFileAgainstCasRecord({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Completed Archive original quarantine ${operation.target}` + }); + } else if (!finalizationStarted) { + throw new Error(`Completed Archive original quarantine ${operation.target} disappeared`); + } + } + return; + } + if (operation.type === "remove") { + await assertContent({ + target, + expectedHash: null, + label: `Completed Archive target ${operation.target}` + }); + await assertContent({ + target: await resolveRef2(paths, operation.backup), + expectedHash: operation.expectedTargetHash, + label: `Completed Archive backup ${operation.backup}` + }); + const cas = await archiveCasPaths(paths, journal, operation, target); + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error(`Completed Archive remove ${operation.target} has no original CAS identity`); + } + if (await pathExists(cas.originalQuarantine)) { + await validateFileAgainstCasRecord({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Completed Archive original quarantine ${operation.target}` + }); + } else if (!finalizationStarted) { + throw new Error(`Completed Archive original quarantine ${operation.target} disappeared`); + } + return; + } + if (finalizationStarted) return; + await assertContent({ + target: await resolveRef2(paths, operation.source), + expectedHash: null, + label: `Completed Archive move source ${operation.source}` + }); + await assertContent({ + target, + expectedHash: operation.expectedSourceHash, + expectedKind: "directory", + label: `Completed Archive move target ${operation.target}` + }); +} +async function applyNativeArchiveTransactionV2(paths, journal, hooks) { + let current = journal.status === "prepared" ? await setStatus(paths, journal, "applying") : journal; + if (current.status !== "applying") { + throw new Error(`Native Archive transaction ${current.id} cannot apply from ${current.status}`); + } + const events = await readNativeTransactionEvents(paths, current.id); + const completedIds = completedOperationIds({ journal: current, events }); + const completed = new Set(completedIds); + const finalizationStarted = events.some((event) => event.type === "archive-finalization-started"); + for (const operation of current.operations.slice(0, completedIds.length)) { + await assertCompletedOperation(paths, current, operation, finalizationStarted); + } + let completedCount = completed.size; + for (const operation of current.operations) { + if (completed.has(operation.id)) continue; + await appendNativeTransactionEvent(paths, current.id, "operation-started", operation.id); + await applyOperation2(paths, current, operation, hooks); + await appendNativeTransactionEvent(paths, current.id, "operation-completed", operation.id); + completedCount += 1; + await hooks?.afterOperation?.(operation, completedCount); + } + current = await readNativeArchiveTransactionV2(paths, current.id); + return current; +} +async function fileMatchesCasRecord(options) { + try { + await validateFileAgainstCasRecord(options); + return true; + } catch { + return false; + } +} +async function installOriginalTarget(options) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive rollback original quarantine ${options.operation.target}` + }); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive rollback target ${options.operation.target}` + }); + await options.hooks?.beforeArchiveTargetInstall?.("rollback", options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive rollback original quarantine ${options.operation.target}` + }); + try { + await fs20.link(options.quarantine, options.target); + } catch (error) { + if (error.code === "EEXIST") { + throw new Error( + `Archive rollback target ${options.operation.target} was created before exclusive restore`, + { cause: error } + ); + } + throw error; + } + await guard.verify(); + await options.hooks?.afterArchiveTargetInstalled?.("rollback", options.operation, options.target); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive rollback target ${options.operation.target}` + }); +} +async function removeWriteCandidateIfPresent(options) { + if (options.operation.type !== "write") return; + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + if (!await pathExists(cas.candidate)) return; + const current = await captureStableArchiveFile({ + paths: options.paths, + file: cas.candidate, + expectedHash: options.operation.stagedHash, + label: `Archive write candidate ${options.operation.target}` + }); + await removeExactCasFile({ + paths: options.paths, + file: cas.candidate, + operation: options.operation, + record: { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: "post", + hash: options.operation.stagedHash, + identity: current.identity + }, + label: `Archive write candidate ${options.operation.target}` + }); +} +async function rollbackWriteOrRemove(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + const cas = await archiveCasPaths(paths, journal, operation, target); + const originalHash = operation.expectedTargetHash; + const originalRecord = originalHash === null ? null : await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: originalHash + }); + let postRecord = operation.type === "write" ? await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }) : null; + if (operation.type === "write" && !postRecord && await pathExists(cas.candidate) && await pathExists(target)) { + const candidate = await captureStableArchiveFile({ + paths, + file: cas.candidate, + expectedHash: operation.stagedHash, + label: `Archive write candidate ${operation.target}` + }); + const current = await captureStableArchiveFile({ + paths, + file: target, + expectedHash: operation.stagedHash, + label: `Archive rollback target ${operation.target}` + }); + if (sameFileObject2(candidate.identity, current.identity)) { + postRecord = await persistCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash, + identity: current.identity + }); + } + } + if (await pathExists(cas.rollbackQuarantine)) { + if (!postRecord) { + throw new Error(`Archive rollback quarantine ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: cas.rollbackQuarantine, + operation, + record: postRecord, + label: `Archive rollback quarantine ${operation.target}` + }); + } + if (await pathExists(target)) { + const alreadyRestored = originalRecord !== null && await fileMatchesCasRecord({ + paths, + file: target, + operation, + record: originalRecord, + label: `Archive rollback target ${operation.target}` + }); + if (!alreadyRestored) { + if (!postRecord) { + if (!originalRecord) { + const actual = await inspectNativeArchiveContent(target); + if (sameContent(actual, originalHash)) { + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + return; + } + } + throw new Error( + `Archive rollback target ${operation.target} is occupied by an external object` + ); + } + await quarantineBoundTarget({ + paths, + operation, + phase: "rollback", + target, + quarantine: cas.rollbackQuarantine, + record: postRecord, + hooks + }); + } + } + if (originalHash === null) { + if (await pathExists(target)) { + throw new Error(`Archive rollback create target ${operation.target} could not be removed`); + } + } else if (!await pathExists(target)) { + if (!originalRecord || !await pathExists(cas.originalQuarantine)) { + throw new Error( + `Archive rollback original target ${operation.target} cannot be recovered safely` + ); + } + await installOriginalTarget({ + paths, + operation, + target, + quarantine: cas.originalQuarantine, + record: originalRecord, + hooks + }); + } + if (originalRecord && await pathExists(cas.originalQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Archive rollback original quarantine ${operation.target}` + }); + } + if (postRecord && await pathExists(cas.rollbackQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.rollbackQuarantine, + operation, + record: postRecord, + label: `Archive rollback quarantine ${operation.target}` + }); + } + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + await assertContent({ + target, + expectedHash: originalHash, + label: `Archive rollback target ${operation.target}` + }); +} +async function rollbackMove(paths, operation) { + const source = await resolveRef2(paths, operation.source); + const target = await resolveRef2(paths, operation.target); + const [sourceContent, targetContent] = await Promise.all([ + inspectNativeArchiveContent(source), + inspectNativeArchiveContent(target) + ]); + if (sameContent(sourceContent, operation.expectedSourceHash, "directory") && targetContent === null) { + return; + } + if (sourceContent !== null || !sameContent(targetContent, operation.expectedSourceHash, "directory")) { + throw new Error( + `Archive rollback move ${operation.id} content changed: source=${contentDescription(sourceContent)}, target=${contentDescription(targetContent)}` + ); + } + await fs20.mkdir(path31.dirname(source), { recursive: true }); + await fs20.rename(target, source); +} +async function rollbackNativeArchiveTransactionV2(paths, journal, hooks) { + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some( + (event) => event.type === "archive-finalization-started" || event.type === "archive-finalized" + )) { + throw new Error("An archive whose finalization started can only be recovered by continuing it"); + } + let current = await setStatus(paths, journal, "rolling-back"); + await appendNativeTransactionEvent(paths, current.id, "rollback-started"); + const started = new Set( + events.filter((event) => event.type === "operation-started" || event.type === "operation-completed").map((event) => event.operationId) + ); + for (const operation of [...current.operations].reverse()) { + if (!started.has(operation.id)) continue; + if (operation.type === "move") await rollbackMove(paths, operation); + else await rollbackWriteOrRemove(paths, current, operation, hooks); + } + await appendNativeTransactionEvent(paths, current.id, "rollback-completed"); + current = await setStatus(paths, current, "rolled-back"); + return current; +} +async function assertCommittedArchiveCasTarget(paths, journal, operation) { + const target = await resolveRef2(paths, operation.target); + if (operation.type === "write") { + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord) { + throw new Error(`Archive commit target ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: target, + operation, + record: postRecord, + label: `Archive commit target ${operation.target}` + }); + return; + } + await assertContent({ + target, + expectedHash: null, + label: `Archive commit target ${operation.target}` + }); +} +async function cleanupCommittedArchiveCas(paths, journal) { + for (const operation of journal.operations) { + if (operation.type === "move") continue; + const target = await resolveRef2(paths, operation.target); + const cas = await archiveCasPaths(paths, journal, operation, target); + if (await pathExists(cas.rollbackQuarantine)) { + throw new Error(`Archive commit found an unfinished rollback for ${operation.target}`); + } + await assertCommittedArchiveCasTarget(paths, journal, operation); + if (operation.expectedTargetHash !== null) { + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error(`Archive commit target ${operation.target} has no original CAS identity`); + } + if (await pathExists(cas.originalQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Archive commit original quarantine ${operation.target}` + }); + } + } + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + } + for (const operation of journal.operations) { + if (operation.type !== "move") { + await assertCommittedArchiveCasTarget(paths, journal, operation); + } + } +} +async function finalizeNativeArchiveTransactionV2(paths, journal, event) { + if (event === "commit") await cleanupCommittedArchiveCas(paths, journal); + await appendNativeTransactionEvent(paths, journal.id, event); + return event === "commit" ? setStatus(paths, journal, "committed") : journal; +} + +// domains/comet-native/native-checkpoint-journal.ts +import { randomUUID as randomUUID4 } from "crypto"; +import { promises as fs21 } from "fs"; + +// domains/comet-native/native-checkpoint-storage.ts +import path32 from "path"; +var NATIVE_CHECKPOINT_LIMITS = { + maxArtifacts: 128, + maxFileBytes: 16 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxDocumentBytes: 256 * 1024 +}; +var HASH_PATTERN13 = /^[a-f0-9]{64}$/u; +function record7(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactKeys3(value, keys, label) { + const expected = new Set(keys); + const unknown = Object.keys(value).filter((key) => !expected.has(key)); + const missing = keys.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function stringValue(value, label, max = 2e3) { + if (typeof value !== "string" || value.length === 0 || value.length > max) { + throw new Error(`${label} must be a non-empty string of at most ${max} characters`); + } + return value; +} +function positiveInteger6(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function nonNegativeInteger3(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function normalizeNativeCheckpointArtifactRef(value) { + const trimmed = value.trim().replaceAll("\\", "/"); + if (trimmed.length === 0 || path32.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|\/)/u.test(trimmed) || trimmed.split("/").includes("..")) { + throw new Error(`Checkpoint artifact must be project-relative: ${value}`); + } + const normalized = path32.posix.normalize(trimmed); + if (normalized === "." || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`Checkpoint artifact must name a project file: ${value}`); + } + return normalized; +} +function nativeProgressCheckpointFile(paths, name) { + return path32.join(nativeChangeDir(paths, name), "runtime", "checkpoints", "progress.json"); +} +function nativeCheckpointJournalFile(paths, name) { + return path32.join(nativeChangeDir(paths, name), "runtime", "checkpoint-journal.json"); +} +function nativeCheckpointManifestFile(paths, name, hash6) { + if (!HASH_PATTERN13.test(hash6)) throw new Error("Native checkpoint manifest hash is invalid"); + return path32.join( + nativeChangeDir(paths, name), + "runtime", + "checkpoints", + "manifests", + `${hash6}.json` + ); +} +function nativeCheckpointManifestRef(hash6) { + if (!HASH_PATTERN13.test(hash6)) throw new Error("Native checkpoint manifest hash is invalid"); + return `runtime/checkpoints/manifests/${hash6}.json`; +} +async function readBoundedJson(root, file, label) { + const snapshot2 = await readNativeProtectedFile({ + root, + file, + maxBytes: NATIVE_CHECKPOINT_LIMITS.maxDocumentBytes, + label + }); + return JSON.parse(snapshot2.bytes.toString("utf8")); +} +function parseArtifact(value, index) { + const artifact = record7(value, `checkpoint manifest artifact ${index}`); + exactKeys3(artifact, ["path", "hash", "size"], `checkpoint manifest artifact ${index}`); + const artifactPath = normalizeNativeCheckpointArtifactRef( + stringValue(artifact.path, `checkpoint artifact ${index} path`, 4096) + ); + const sensitiveReason = nativeSensitiveRelativePathReason(artifactPath); + if (sensitiveReason) { + throw new Error( + `checkpoint artifact ${index} is excluded as sensitive (${sensitiveReason}): ${artifactPath}` + ); + } + if (typeof artifact.hash !== "string" || !HASH_PATTERN13.test(artifact.hash)) { + throw new Error(`checkpoint artifact ${index} hash is invalid`); + } + return { + path: artifactPath, + hash: artifact.hash, + size: nonNegativeInteger3(artifact.size, `checkpoint artifact ${index} size`) + }; +} +function parseNativeCheckpointManifestValue(value, expectedName) { + const manifest = record7(value, "Native checkpoint manifest"); + exactKeys3( + manifest, + ["schema", "change", "artifacts", "totalBytes"], + "Native checkpoint manifest" + ); + if (manifest.schema !== "comet.native.checkpoint-manifest.v1") { + throw new Error("Native checkpoint manifest schema is invalid"); + } + if (manifest.change !== expectedName) + throw new Error("Native checkpoint manifest change mismatch"); + if (!Array.isArray(manifest.artifacts)) { + throw new Error("Native checkpoint manifest artifacts must be an array"); + } + if (manifest.artifacts.length > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error("Native checkpoint manifest has too many artifacts"); + } + const artifacts = manifest.artifacts.map(parseArtifact); + const sorted = [...artifacts].sort((left, right) => left.path.localeCompare(right.path)); + if (JSON.stringify(artifacts) !== JSON.stringify(sorted)) { + throw new Error("Native checkpoint manifest artifacts must be sorted"); + } + if (new Set(artifacts.map((artifact) => artifact.path)).size !== artifacts.length) { + throw new Error("Native checkpoint manifest has duplicate artifacts"); + } + const totalBytes = nonNegativeInteger3(manifest.totalBytes, "checkpoint manifest totalBytes"); + if (artifacts.reduce((total, artifact) => total + artifact.size, 0) !== totalBytes) { + throw new Error("Native checkpoint manifest totalBytes mismatch"); + } + if (totalBytes > NATIVE_CHECKPOINT_LIMITS.maxTotalBytes) { + throw new Error("Native checkpoint manifest totalBytes exceeds its budget"); + } + return { + schema: "comet.native.checkpoint-manifest.v1", + change: expectedName, + artifacts, + totalBytes + }; +} +function hashNativeCheckpointManifest(manifest) { + return sha256Text(JSON.stringify(parseNativeCheckpointManifestValue(manifest, manifest.change))); +} +function parseNativeProgressCheckpointValue(value, expectedName) { + const checkpoint = record7(value, "Native progress checkpoint"); + exactKeys3( + checkpoint, + [ + "schema", + "id", + "change", + "phase", + "previousRevision", + "stateRevision", + "summary", + "nextAction", + "inputHash", + "manifestHash", + "manifestRef", + "artifactCount", + "createdAt" + ], + "Native progress checkpoint" + ); + if (checkpoint.schema !== "comet.native.progress-checkpoint.v1") { + throw new Error("Native progress checkpoint schema is invalid"); + } + if (checkpoint.change !== expectedName) throw new Error("Native checkpoint change mismatch"); + const phase = checkpoint.phase; + if (phase !== "shape" && phase !== "build" && phase !== "verify" && phase !== "archive") { + throw new Error("Native checkpoint phase is invalid"); + } + const previousRevision = positiveInteger6( + checkpoint.previousRevision, + "Native checkpoint previousRevision" + ); + const stateRevision = positiveInteger6( + checkpoint.stateRevision, + "Native checkpoint stateRevision" + ); + if (stateRevision !== previousRevision + 1) { + throw new Error("Native checkpoint stateRevision must increment previousRevision once"); + } + const manifestHash = stringValue(checkpoint.manifestHash, "Native checkpoint manifestHash", 64); + if (!HASH_PATTERN13.test(manifestHash)) + throw new Error("Native checkpoint manifestHash is invalid"); + const expectedManifestRef = nativeCheckpointManifestRef(manifestHash); + if (checkpoint.manifestRef !== expectedManifestRef) { + throw new Error("Native checkpoint manifestRef does not match manifestHash"); + } + const inputHash = stringValue(checkpoint.inputHash, "Native checkpoint inputHash", 64); + if (!HASH_PATTERN13.test(inputHash)) throw new Error("Native checkpoint inputHash is invalid"); + const createdAt = stringValue(checkpoint.createdAt, "Native checkpoint createdAt", 64); + if (Number.isNaN(Date.parse(createdAt))) + throw new Error("Native checkpoint createdAt is invalid"); + const artifactCount = nonNegativeInteger3( + checkpoint.artifactCount, + "Native checkpoint artifactCount" + ); + if (artifactCount > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error("Native checkpoint artifactCount exceeds its budget"); + } + const summary = stringValue(checkpoint.summary, "Native checkpoint summary"); + const nextAction = stringValue(checkpoint.nextAction, "Native checkpoint nextAction"); + if (redactNativeCredentialText(summary) !== summary || redactNativeCredentialText(nextAction) !== nextAction) { + throw new Error("Native checkpoint text contains unredacted credential material"); + } + return { + schema: "comet.native.progress-checkpoint.v1", + id: stringValue(checkpoint.id, "Native checkpoint id", 128), + change: expectedName, + phase, + previousRevision, + stateRevision, + summary, + nextAction, + inputHash, + manifestHash, + manifestRef: expectedManifestRef, + artifactCount, + createdAt + }; +} +function parseNativeCheckpointJournalValue(value, expectedName) { + const journal = record7(value, "Native checkpoint journal"); + exactKeys3( + journal, + [ + "schema", + "id", + "change", + "inputHash", + "createdAt", + "previousState", + "nextState", + "checkpoint", + "manifest" + ], + "Native checkpoint journal" + ); + if (journal.schema !== "comet.native.checkpoint-journal.v1") { + throw new Error("Native checkpoint journal schema is invalid"); + } + if (journal.change !== expectedName) throw new Error("Native checkpoint journal change mismatch"); + const previousState = parseNativeChangeValue(journal.previousState); + const nextState = parseNativeChangeValue(journal.nextState); + const checkpoint = parseNativeProgressCheckpointValue(journal.checkpoint, expectedName); + const manifest = parseNativeCheckpointManifestValue(journal.manifest, expectedName); + const inputHash = stringValue(journal.inputHash, "Native checkpoint journal inputHash", 64); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: checkpoint.summary, + nextAction: checkpoint.nextAction, + artifacts: manifest.artifacts + }) + ); + if (!HASH_PATTERN13.test(inputHash) || inputHash !== checkpoint.inputHash || inputHash !== expectedInputHash || journal.id !== checkpoint.id || journal.createdAt !== checkpoint.createdAt) { + throw new Error("Native checkpoint journal envelope mismatch"); + } + if (previousState.name !== expectedName || nextState.name !== expectedName || nextState.revision !== previousState.revision + 1 || checkpoint.previousRevision !== previousState.revision || checkpoint.stateRevision !== nextState.revision || checkpoint.phase !== nextState.phase || checkpoint.manifestHash !== hashNativeCheckpointManifest(manifest) || checkpoint.artifactCount !== manifest.artifacts.length) { + throw new Error("Native checkpoint journal state mismatch"); + } + return { + schema: "comet.native.checkpoint-journal.v1", + id: checkpoint.id, + change: expectedName, + inputHash, + createdAt: checkpoint.createdAt, + previousState, + nextState, + checkpoint, + manifest + }; +} +async function hashProjectArtifact(paths, artifactRef, hooks) { + const target = path32.resolve(paths.projectRoot, ...artifactRef.split("/")); + if (!isInsidePath(paths.projectRoot, target) || isInsidePath(paths.nativeRoot, target)) { + throw new Error(`Checkpoint artifact is outside project content: ${artifactRef}`); + } + const sensitiveReason = nativeSensitiveArtifactReason(paths, artifactRef); + if (sensitiveReason) { + throw new Error( + `Checkpoint artifact is excluded as sensitive (${sensitiveReason}): ${artifactRef}` + ); + } + const snapshot2 = await readNativeProtectedFile({ + root: paths.projectRoot, + file: target, + maxBytes: NATIVE_CHECKPOINT_LIMITS.maxFileBytes, + label: `Checkpoint artifact ${artifactRef}`, + forbiddenRoots: [paths.nativeRoot], + hooks: { + afterParentChainCaptured: () => hooks?.afterParentChainCaptured?.(artifactRef), + afterOpen: () => hooks?.afterOpen?.(artifactRef), + beforeRead: () => hooks?.beforeRead?.(artifactRef) + } + }); + return { path: artifactRef, hash: snapshot2.hash, size: snapshot2.size }; +} +async function createNativeCheckpointManifest(paths, name, artifactRefs, hooks) { + const normalized = artifactRefs.map(normalizeNativeCheckpointArtifactRef).sort(); + if (normalized.length > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error( + `Checkpoint supports at most ${NATIVE_CHECKPOINT_LIMITS.maxArtifacts} artifacts` + ); + } + if (new Set(normalized).size !== normalized.length) { + throw new Error("Checkpoint artifacts must not contain duplicates"); + } + const artifacts = []; + let totalBytes = 0; + for (const artifactRef of normalized) { + const artifact = await hashProjectArtifact(paths, artifactRef, hooks); + totalBytes += artifact.size; + if (totalBytes > NATIVE_CHECKPOINT_LIMITS.maxTotalBytes) { + throw new Error("Checkpoint artifacts exceed the total byte budget"); + } + artifacts.push(artifact); + } + return { + schema: "comet.native.checkpoint-manifest.v1", + change: name, + artifacts, + totalBytes + }; +} +async function readNativeProgressCheckpoint(paths, name) { + const file = nativeProgressCheckpointFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + return parseNativeProgressCheckpointValue( + await readBoundedJson(paths.nativeRoot, file, "Native progress checkpoint"), + name + ); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function readNativeCheckpointManifest(paths, name, hash6) { + const file = nativeCheckpointManifestFile(paths, name, hash6); + await resolveContainedNativePath(paths.nativeRoot, file); + const value = await readBoundedJson(paths.nativeRoot, file, "Native checkpoint manifest"); + const manifest = parseNativeCheckpointManifestValue(value, name); + assertCheckpointManifestSafeForPaths(paths, manifest); + if (hashNativeCheckpointManifest(manifest) !== hash6) { + throw new Error("Native checkpoint manifest content hash mismatch"); + } + return manifest; +} +async function readNativeCheckpointJournal(paths, name) { + const file = nativeCheckpointJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const journal = parseNativeCheckpointJournalValue( + await readBoundedJson(paths.nativeRoot, file, "Native checkpoint journal"), + name + ); + assertCheckpointManifestSafeForPaths(paths, journal.manifest); + return journal; + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function writeNativeCheckpointManifest(paths, name, manifest, hooks) { + const parsed = parseNativeCheckpointManifestValue(manifest, name); + assertCheckpointManifestSafeForPaths(paths, parsed); + const hash6 = hashNativeCheckpointManifest(parsed); + const file = nativeCheckpointManifestFile(paths, name, hash6); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const existing = await readNativeCheckpointManifest(paths, name, hash6); + if (JSON.stringify(existing) !== JSON.stringify(parsed)) { + throw new Error("Native checkpoint manifest hash collision"); + } + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, parsed, { + containedRoot: paths.nativeRoot, + beforeCommit: hooks?.beforeCommit + }); + return hash6; +} +function assertCheckpointManifestSafeForPaths(paths, manifest) { + for (const artifact of manifest.artifacts) { + const reason = nativeSensitiveArtifactReason(paths, artifact.path); + if (reason) { + throw new Error( + `Native checkpoint manifest contains a sensitive artifact (${reason}): ${artifact.path}` + ); + } + } +} +async function writeNativeProgressCheckpoint(paths, checkpoint) { + const parsed = parseNativeProgressCheckpointValue(checkpoint, checkpoint.change); + const manifest = await readNativeCheckpointManifest(paths, parsed.change, parsed.manifestHash); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: parsed.summary, + nextAction: parsed.nextAction, + artifacts: manifest.artifacts + }) + ); + if (parsed.inputHash !== expectedInputHash || parsed.artifactCount !== manifest.artifacts.length) { + throw new Error("Native progress checkpoint does not match its artifact manifest"); + } + const file = nativeProgressCheckpointFile(paths, checkpoint.change); + await resolveContainedNativePath(paths.nativeRoot, file); + await atomicWriteJson(file, parsed, { containedRoot: paths.nativeRoot }); +} +async function writeNativeCheckpointJournal(paths, journal) { + const parsed = parseNativeCheckpointJournalValue(journal, journal.change); + const file = nativeCheckpointJournalFile(paths, journal.change); + await resolveContainedNativePath(paths.nativeRoot, file); + if (await readNativeCheckpointJournal(paths, journal.change)) { + throw new Error(`Native checkpoint recovery is already pending for ${journal.change}`); + } + await atomicWriteJson(file, parsed, { containedRoot: paths.nativeRoot }); +} +async function inspectNativeCheckpointFreshness(options) { + let checkpoint; + try { + checkpoint = await readNativeProgressCheckpoint(options.paths, options.name); + } catch (error) { + return { + checkpoint: null, + manifest: null, + freshness: "stale", + reasons: ["checkpoint-progress-invalid"], + findings: [ + { + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint is invalid: ${error.message}. Automatic repair is unavailable; inspect and move the invalid checkpoint file aside before retrying`, + path: nativeProgressCheckpointFile(options.paths, options.name) + } + ] + }; + } + if (!checkpoint) { + return { + checkpoint: null, + manifest: null, + freshness: "fresh", + reasons: ["no-checkpoint"], + findings: [] + }; + } + const reasons = []; + const findings = []; + if (checkpoint.stateRevision !== options.stateRevision) reasons.push("state-revision-changed"); + let manifest = null; + try { + manifest = await readNativeCheckpointManifest( + options.paths, + options.name, + checkpoint.manifestHash + ); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: checkpoint.summary, + nextAction: checkpoint.nextAction, + artifacts: manifest.artifacts + }) + ); + if (checkpoint.inputHash !== expectedInputHash || checkpoint.artifactCount !== manifest.artifacts.length) { + throw new Error("Native progress checkpoint does not match its artifact manifest"); + } + for (const expected of manifest.artifacts) { + try { + const actual = await hashProjectArtifact(options.paths, expected.path); + if (actual.hash !== expected.hash || actual.size !== expected.size) { + reasons.push(`artifact-changed:${expected.path}`); + } + } catch { + reasons.push(`artifact-unavailable:${expected.path}`); + } + } + } catch (error) { + reasons.push("checkpoint-manifest-invalid"); + findings.push({ + code: "checkpoint-manifest-invalid", + message: `Native checkpoint manifest is invalid: ${error.message}`, + path: nativeCheckpointManifestFile(options.paths, options.name, checkpoint.manifestHash) + }); + } + return { + checkpoint, + manifest, + freshness: reasons.length === 0 ? "fresh" : "stale", + reasons, + findings + }; +} + +// domains/comet-native/native-checkpoint-journal.ts +async function prepareNativeCheckpointJournal(options) { + const createdAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString(); + const id = options.checkpointId?.() ?? randomUUID4(); + const checkpoint = { + ...options.checkpoint, + id, + createdAt + }; + const journal = { + schema: "comet.native.checkpoint-journal.v1", + id, + change: options.previousState.name, + inputHash: checkpoint.inputHash, + createdAt, + previousState: options.previousState, + nextState: options.nextState, + checkpoint, + manifest: options.manifest + }; + await writeNativeCheckpointManifest(options.paths, options.previousState.name, options.manifest); + await writeNativeCheckpointJournal(options.paths, journal); + return journal; +} +async function continueNativeCheckpointLocked(paths, name, hooks) { + const journal = await readNativeCheckpointJournal(paths, name); + if (!journal) return null; + const manifestHash = await writeNativeCheckpointManifest(paths, journal.change, journal.manifest); + if (manifestHash !== journal.checkpoint.manifestHash) { + throw new Error("Native checkpoint recovery manifest hash mismatch"); + } + await compareAndSwapNativeChangeLocked(paths, journal.nextState, journal.previousState.revision, { + allowPendingCheckpointRecovery: true + }); + await hooks?.afterStateWritten?.(journal); + await writeNativeProgressCheckpoint(paths, journal.checkpoint); + await hooks?.afterProgressWritten?.(journal); + await fs21.rm(nativeCheckpointJournalFile(paths, name), { force: true }); + return journal; +} +async function continueNativeCheckpoint(paths, name, hooks) { + return withNativeMutationLock( + paths, + `continue checkpoint ${name}`, + () => withNativeTransitionLock(paths, name, `continue checkpoint ${name}`, async () => { + await continueNativeTransitionLocked(paths, name); + return continueNativeCheckpointLocked(paths, name, hooks); + }) + ); +} + +// domains/comet-native/native-change-recovery.ts +async function settleNativeChangeJournalsLocked(paths, name) { + await continueNativeTransitionLocked(paths, name); + await continueNativeCheckpointLocked(paths, name); +} + +// domains/comet-entry/current-selection.ts +import { randomUUID as randomUUID5 } from "crypto"; +import { promises as fs22 } from "fs"; +import path33 from "path"; +var COMET_CURRENT_SELECTION_SCHEMA = "comet.selection.v2"; +var COMET_CURRENT_SELECTION_MAX_BYTES = 16 * 1024; +function cometCurrentSelectionFile(projectRoot) { + return path33.join(projectRoot, ".comet", "current-change.json"); +} +function isRecord(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function validBranch(value) { + return value === null || typeof value === "string"; +} +function parseSelection(source) { + let value; + try { + value = JSON.parse(source); + } catch (error) { + throw new Error( + `current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + if (!isRecord(value)) { + throw new Error("current change selection must be a JSON object"); + } + if (value.version === 1) { + if (typeof value.change !== "string") { + throw new Error("legacy current change selection change must be a string"); + } + if (value.branch !== void 0 && !validBranch(value.branch)) { + throw new Error("legacy current change selection branch must be a string or null"); + } + return { + selection: { + schema: COMET_CURRENT_SELECTION_SCHEMA, + workflow: "classic", + change: value.change, + branch: value.branch ?? null + }, + legacy: true + }; + } + if (value.schema !== COMET_CURRENT_SELECTION_SCHEMA) { + throw new Error(`current change selection schema must be ${COMET_CURRENT_SELECTION_SCHEMA}`); + } + if (value.workflow !== "native" && value.workflow !== "classic") { + throw new Error("current change selection workflow must be native or classic"); + } + if (typeof value.change !== "string") { + throw new Error("current change selection change must be a string"); + } + if (!validBranch(value.branch)) { + throw new Error("current change selection branch must be a string or null"); + } + if (value.workflow === "native" && value.branch !== null) { + throw new Error("Native current change selection branch must be null"); + } + return { selection: value, legacy: false }; +} +async function readCometCurrentSelection(projectRoot) { + let source; + try { + const file = cometCurrentSelectionFile(projectRoot); + const { bytes } = await readFileRaceSafe(file, COMET_CURRENT_SELECTION_MAX_BYTES, { + label: "current change selection" + }); + source = bytes.toString("utf8"); + } catch (error) { + if (error.code === "ENOENT") return { status: "missing" }; + throw new Error( + `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + const parsed = parseSelection(source); + return { status: "selected", ...parsed }; +} +async function writeCometCurrentSelection(projectRoot, selection) { + const parsed = parseSelection(JSON.stringify(selection)); + if (parsed.legacy) throw new Error("cannot write a legacy current change selection"); + const file = cometCurrentSelectionFile(projectRoot); + const temporary = `${file}.${randomUUID5()}.tmp`; + await fs22.mkdir(path33.dirname(file), { recursive: true }); + try { + await fs22.writeFile(temporary, `${JSON.stringify(parsed.selection, null, 2)} +`, "utf8"); + await fs22.rename(temporary, file); + } catch (error) { + await fs22.rm(temporary, { force: true }); + throw error; + } +} +async function clearCometCurrentSelection(projectRoot) { + await fs22.rm(cometCurrentSelectionFile(projectRoot), { force: true }); +} +async function clearCometCurrentSelectionIf(projectRoot, workflow, change) { + const current = await readCometCurrentSelection(projectRoot); + if (current.status !== "selected" || current.selection.workflow !== workflow || current.selection.change !== change) { + return false; + } + await clearCometCurrentSelection(projectRoot); + return true; +} + +// domains/comet-native/native-selection.ts +var NATIVE_SELECTION_MAX_BYTES = 16 * 1024; +async function readNativeSelectionRecord(paths) { + const current = await readCometCurrentSelection(paths.projectRoot); + if (current.status === "missing" || current.selection.workflow !== "native") return null; + assertNativeName(current.selection.change); + return current.selection; +} +function nativeSelectionFile(paths) { + return cometCurrentSelectionFile(paths.projectRoot); +} +async function selectNativeChange(paths, name) { + return withNativeMutationLock(paths, `select change ${name}`, async () => { + assertNativeName(name); + await readNativeChange(paths, name); + await writeCometCurrentSelection(paths.projectRoot, { + schema: "comet.selection.v2", + workflow: "native", + change: name, + branch: null + }); + }); +} +async function resolveSelectedNativeChange(paths) { + const value = await readNativeSelectionRecord(paths); + if (!value) return null; + await readNativeChange(paths, value.change); + return value.change; +} +async function clearNativeSelectionIfLocked(paths, name) { + await assertNoPendingNativeRootMove(paths.projectRoot); + return clearCometCurrentSelectionIf(paths.projectRoot, "native", name); +} + +// domains/comet-native/native-archive.ts +var NATIVE_ARCHIVE_COPY_MAX_BYTES2 = 16 * 1024 * 1024; +var NativeSpecConflictError = class extends Error { + constructor(capability, expectedHash, actualHash, canonicalPath) { + super( + `Canonical spec conflict for ${capability}: expected ${expectedHash ?? "(missing)"}, actual ${actualHash ?? "(missing)"}` + ); + this.capability = capability; + this.expectedHash = expectedHash; + this.actualHash = actualHash; + this.canonicalPath = canonicalPath; + this.name = "NativeSpecConflictError"; + } + capability; + expectedHash; + actualHash; + canonicalPath; + code = "native-spec-conflict"; +}; +var NativeArchivePreflightError = class extends Error { + constructor(preflight, message = preflight.ready ? "Native Archive preflight no longer matches the expected hash" : `Native Archive preflight is blocked: ${preflight.findingCodes.join(", ")}`) { + super(message); + this.preflight = preflight; + this.name = "NativeArchivePreflightError"; + } + preflight; + code = "native-archive-preflight"; +}; +async function optionalHash2(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +function assertArchiveReady(state) { + if (state.phase !== "archive") throw new Error(`Native change ${state.name} is not in Archive`); + if (state.verification_result !== "pass") { + throw new Error(`Native change ${state.name} has not passed verification`); + } + if (!state.verification_report) { + throw new Error(`Native change ${state.name} has no verification report`); + } + if (state.archived) throw new Error(`Native change ${state.name} is already archived`); +} +async function assertArchiveArtifacts(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const brief = await validateNativeBrief(changeDir, state.brief); + const verification = await validateNativeVerification(changeDir, state.verification_report); + const findings = [...brief.findings, ...verification.findings]; + if (findings.length > 0) { + throw new Error(`Native archive artifacts are invalid: ${findings[0].message}`); + } +} +async function assertSpecBase(paths, change) { + const canonical = canonicalSpecPath(paths, change.capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const actual = await optionalHash2(canonical); + const expected = change.operation === "create" ? null : change.base_hash; + if (actual !== expected) { + throw new NativeSpecConflictError(change.capability, expected, actual, canonical); + } +} +function archiveTarget(paths, name, now) { + return path34.join(paths.archiveDir, `${now.toISOString().slice(0, 10)}-${name}`); +} +async function pathExists2(target) { + try { + await fs23.access(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function buildArchiveJournal(options) { + const { paths, state, now, transactionId, preflight } = options; + const target = archiveTarget(paths, state.name, now); + if (await pathExists2(target)) throw new Error(`Native archive target already exists: ${target}`); + if (nativeRootRef(paths, target) !== preflight.targetRef) { + throw new Error("Native Archive target changed after preflight"); + } + const tx = await resolveNativeTransactionPaths(paths, transactionId); + const operations = []; + for (const [index, change] of state.spec_changes.entries()) { + await assertSpecBase(paths, change); + const canonical = canonicalSpecPath(paths, change.capability); + const preview = preflight.operations.find( + (operation) => operation.capability === change.capability && operation.operation === change.operation + ); + if (!preview) { + throw new Error(`Native Archive preflight has no operation for ${change.capability}`); + } + if (change.operation !== "remove" && preview.proposedHash === null) { + throw new Error(`Native Archive preflight has no proposed hash for ${change.capability}`); + } + const backup = path34.join(tx.backups, "specs", change.capability, "spec.md"); + if (change.operation === "remove") { + operations.push({ + id: `spec-${index + 1}-${change.capability}`, + type: "remove", + target: nativeRootRef(paths, canonical), + backup: nativeRootRef(paths, backup), + expectedTargetHash: change.base_hash + }); + continue; + } + const changeDir = nativeChangeDir(paths, state.name); + await resolveNativeArtifactFile(changeDir, change.source); + const source2 = path34.resolve(changeDir, ...change.source.split(/[\\/]/u)); + const staged = path34.join(tx.staged, "specs", change.capability, "spec.md"); + const stagedSnapshot = await copyNativeProtectedFile({ + sourceRoot: changeDir, + source: source2, + targetRoot: paths.nativeRoot, + target: staged, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES2, + label: `Native Archive proposed spec ${change.capability}`, + expectedHash: preview.proposedHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => options.hooks?.afterProtectedCopySourceParentCaptured?.("stage", change.source) + } + }); + const stagedHash = stagedSnapshot.hash; + if (stagedHash !== preview.proposedHash) { + throw new Error(`Proposed Native spec changed after preflight: ${change.capability}`); + } + operations.push({ + id: `spec-${index + 1}-${change.capability}`, + type: "write", + target: nativeRootRef(paths, canonical), + staged: nativeRootRef(paths, staged), + ...change.operation === "replace" ? { backup: nativeRootRef(paths, backup) } : {}, + expectedTargetHash: change.operation === "create" ? null : change.base_hash, + stagedHash + }); + } + const source = nativeChangeDir(paths, state.name); + operations.push({ + id: "archive-change", + type: "move", + source: nativeRootRef(paths, source), + target: nativeRootRef(paths, target), + expectedSourceHash: await hashNativeArchiveTree(source), + expectedTargetHash: null + }); + return { + schema: "comet.native.transaction.v2", + id: transactionId, + kind: "archive", + status: "prepared", + change: state.name, + createdAt: now.toISOString(), + preflightHash: preflight.preflightHash, + operations + }; +} +function archiveDirectoryFromJournal(paths, journal) { + const operation = journal.operations.find((item) => item.id === "archive-change"); + if (!operation || operation.type !== "move") { + throw new Error(`Archive transaction ${journal.id} has no archive move`); + } + return path34.resolve(paths.nativeRoot, ...operation.target.split("/")); +} +async function finalizeArchive(paths, journal, hooks) { + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some((event2) => event2.type === "archive-finalized")) return; + const finalizationStarted = events.some((event2) => event2.type === "archive-finalization-started"); + if (journal.schema === "comet.native.transaction.v2" && !finalizationStarted) { + const move = journal.operations.find((operation) => operation.id === "archive-change"); + if (!move || move.type !== "move" || !move.expectedSourceHash) { + throw new Error(`Archive transaction ${journal.id} has no content-bound archive move`); + } + const archiveContent = await inspectNativeArchiveContent( + archiveDirectoryFromJournal(paths, journal) + ); + if (archiveContent?.kind !== "directory" || archiveContent.hash !== move.expectedSourceHash) { + throw new Error( + `Native Archive content changed before finalization for transaction ${journal.id}` + ); + } + } + const archiveDir = archiveDirectoryFromJournal(paths, journal); + const stateFile = path34.join(archiveDir, NATIVE_CHANGE_STATE_FILE); + const state = await readNativeChangeFile(stateFile); + if (!journal.change || state.name !== journal.change) { + throw new Error(`Archive transaction ${journal.id} change mismatch`); + } + const run = await readNativeRunState(archiveDir); + if (!run || run.runId !== state.run_id || run.currentStep !== "archive" && !(run.currentStep === null && run.status === "completed")) { + throw new Error(`Native archive Run state is missing or inconsistent for ${state.name}`); + } + let completed = run; + if (run.currentStep === "archive") { + const decision = decideWithResolver( + NATIVE_RUNTIME_PACKAGE, + run, + /* @__PURE__ */ new Set(), + nativePhaseResolver, + void 0 + ); + if (!decision.action) throw new Error(decision.reason ?? "Native archive produced no action"); + completed = recordOutcomeWithResolver( + NATIVE_RUNTIME_PACKAGE, + decision.state, + { + actionId: decision.action.id, + status: "succeeded", + summary: `Archived Native change ${state.name}` + }, + nativePhaseResolver, + void 0 + ); + } + const evidenceHash = sha256Text(`archive:${journal.id}:${state.name}`); + const trajectory = await readNativeTrajectory(archiveDir, completed.trajectoryRef); + const transactionEvents = trajectory.filter((item) => item.data.transactionId === journal.id); + if (journal.schema === "comet.native.transaction.v2" && (transactionEvents.length > 1 || transactionEvents.some((item) => item.type !== "state_transitioned"))) { + throw new Error(`Native Archive trajectory has a transaction id collision: ${journal.id}`); + } + let event = transactionEvents.find((item) => item.type === "state_transitioned"); + const eventData = { + previousPhase: "archive", + nextPhase: null, + evidenceHash, + summary: `Archived Native change ${state.name}`, + transactionId: journal.id + }; + if (event && journal.schema === "comet.native.transaction.v2" && (!isDeepStrictEqual2(event.data, eventData) || event.runId !== completed.runId || event.timestamp !== journal.createdAt || event !== trajectory.at(-1))) { + throw new Error(`Native Archive trajectory event changed for transaction ${journal.id}`); + } + if (!finalizationStarted && (state.archived || run.currentStep === null || transactionEvents.length > 0)) { + throw new Error( + `Native Archive finalization state changed before its irreversible marker: ${journal.id}` + ); + } + if (!finalizationStarted) { + if (journal.schema === "comet.native.transaction.v2") { + await finalizeNativeArchiveTransactionV2(paths, journal, "archive-finalization-started"); + await hooks?.afterFinalizationStarted?.(journal); + } else { + await finalizeNativeTransaction(paths, journal, "archive-finalization-started"); + } + } + if (!state.archived) { + const updated = { ...state, archived: true }; + await writeNativeChangeFile(stateFile, updated); + } + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir: archiveDir, + run: completed, + type: "state_transitioned", + data: eventData, + ...journal.schema === "comet.native.transaction.v2" ? { now: new Date(journal.createdAt) } : {} + }); + } + await writeNativeCheckpoint2({ + changeDir: archiveDir, + run: completed, + trajectoryOffset: event.sequence, + evidenceHash, + ...journal.schema === "comet.native.transaction.v2" ? { now: new Date(journal.createdAt) } : {} + }); + await writeNativeRunState(archiveDir, completed); + await clearNativeSelectionIfLocked(paths, state.name); + if (journal.schema === "comet.native.transaction.v2") { + await finalizeNativeArchiveTransactionV2(paths, journal, "archive-finalized"); + } else { + await finalizeNativeTransaction(paths, journal, "archive-finalized"); + } +} +async function continueArchive(paths, journal, hooks) { + if (journal.schema === "comet.native.transaction.v2") { + const events = await readNativeTransactionEvents(paths, journal.id); + if (!events.some((event) => event.type === "operation-started")) { + const preflight = await inspectNativeArchivePreflight({ + paths, + name: journal.change, + now: new Date(journal.createdAt) + }); + if (!preflight.ready || preflight.preflightHash !== journal.preflightHash) { + throw new NativeArchivePreflightError( + preflight, + "Native Archive facts changed before the first transaction operation" + ); + } + } + const applied2 = await applyNativeArchiveTransactionV2(paths, journal, hooks); + await finalizeArchive(paths, applied2, hooks); + return finalizeNativeArchiveTransactionV2(paths, applied2, "commit"); + } + const applied = await applyNativeTransaction(paths, journal); + await finalizeArchive(paths, applied, hooks); + return finalizeNativeTransaction(paths, applied, "commit"); +} +function assertMatchingJournal(paths, journal) { + if (journal.kind !== "archive") throw new Error(`Transaction ${journal.id} is not an archive`); + if (journal.schema === "comet.native.transaction.v2") { + if (!journal.change) throw new Error(`Archive transaction ${journal.id} has no change`); + return; + } + if (path34.resolve(journal.projectRoot) !== path34.resolve(paths.projectRoot) || path34.resolve(journal.nativeRoot) !== path34.resolve(paths.nativeRoot)) { + throw new Error(`Transaction ${journal.id} belongs to a different Native root`); + } +} +async function archiveNativeChange(options) { + return withNativeMutationLock( + options.paths, + `archive ${options.name}`, + () => withNativeTransitionLock(options.paths, options.name, `archive ${options.name}`, async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const lock = await acquireNativeLock(options.paths, "archive", `archive ${options.name}`); + try { + if (!/^[a-f0-9]{64}$/u.test(options.expectedPreflightHash)) { + throw new Error("Native Archive expected preflight must be a SHA-256 hash"); + } + const now = options.now ?? /* @__PURE__ */ new Date(); + const preflight = await inspectNativeArchivePreflight({ + paths: options.paths, + name: options.name, + now + }); + if (!preflight.ready || preflight.preflightHash !== options.expectedPreflightHash) { + throw new NativeArchivePreflightError(preflight); + } + const state = await readNativeChange(options.paths, options.name); + assertArchiveReady(state); + await assertArchiveArtifacts(options.paths, state); + const transactionId = randomUUID6(); + const journal = await buildArchiveJournal({ + paths: options.paths, + state, + now, + transactionId, + preflight, + hooks: options.hooks + }); + await createNativeArchiveTransactionV2(options.paths, journal); + await options.hooks?.afterPrepared?.(journal); + await continueArchive(options.paths, journal, options.hooks); + return { + archiveDir: archiveDirectoryFromJournal(options.paths, journal), + transactionId, + preflightHash: preflight.preflightHash + }; + } finally { + await releaseNativeLock(lock); + } + }) + ); +} +async function recoverArchiveTransaction(options) { + return withNativeMutationLock( + options.paths, + `recover archive ${options.transactionId}`, + async () => { + const lock = await acquireNativeLock( + options.paths, + "archive", + `recover archive ${options.transactionId}` + ); + try { + const generic = await readNativeTransaction(options.paths, options.transactionId); + const journal = generic.schema === "comet.native.transaction.v2" ? await readNativeArchiveTransactionV2(options.paths, options.transactionId) : generic; + assertMatchingJournal(options.paths, journal); + if (journal.status === "committed" || journal.status === "rolled-back") return journal; + return options.strategy === "continue" ? continueArchive(options.paths, journal) : journal.schema === "comet.native.transaction.v2" ? rollbackNativeArchiveTransactionV2(options.paths, journal) : rollbackNativeTransaction(options.paths, journal); + } finally { + await releaseNativeLock(lock); + } + }, + { allowedTransactionId: options.transactionId } + ); +} + +// domains/comet-native/native-diagnostics.ts +import { promises as fs24 } from "fs"; + +// domains/comet-native/native-run-consistency.ts +import path35 from "path"; +function runPath(changeDir, ref) { + return path35.resolve(changeDir, ...ref.split(/[\\/]/u)); +} +async function inspectNativeRunConsistency(paths, state) { + const findings = []; + const changeDir = nativeChangeDir(paths, state.name); + const stateFile = runPath(changeDir, NATIVE_RUN_STORAGE.stateRef); + let run; + try { + run = await readNativeRunState(changeDir); + } catch (error) { + return [ + { + code: "run-state-invalid", + message: `Native Run state is invalid: ${error.message}`, + path: stateFile + } + ]; + } + if (!run) { + if (state.run_id !== null || state.phase !== "shape") { + findings.push({ + code: "run-state-missing", + message: "Native change references a missing Run state", + path: stateFile + }); + } + return findings; + } + if (state.run_id === null) { + return [ + { + code: "run-state-unexpected", + message: "Native change has a Run state but no run_id", + path: stateFile + } + ]; + } + if (run.runId !== state.run_id) { + findings.push({ + code: "run-id-mismatch", + message: `Native Run id ${run.runId} does not match change run_id ${state.run_id}`, + path: stateFile + }); + } + if (run.pending || run.status === "waiting") { + findings.push({ + code: "run-action-pending", + message: "Native Run has an unresolved pending action", + path: stateFile + }); + } + if (run.currentStep !== state.phase) { + findings.push({ + code: "run-phase-mismatch", + message: `Native Run step ${run.currentStep ?? "(none)"} does not match phase ${state.phase}`, + path: stateFile + }); + } + const trajectoryFile2 = runPath(changeDir, run.trajectoryRef); + const tailInspection = await inspectNativeTrajectoryTail(paths, state.name); + if (tailInspection.status === "repairable") { + findings.push({ + code: "trajectory-tail-incomplete", + message: `Native trajectory final line is incomplete at line ${tailInspection.line}; doctor repair can discard ${tailInspection.discardedBytes} incomplete byte(s)`, + path: trajectoryFile2 + }); + return findings; + } + if (tailInspection.status === "invalid") { + findings.push({ + code: "trajectory-invalid", + message: `Native trajectory is invalid at line ${tailInspection.line}: ${tailInspection.message}`, + path: trajectoryFile2 + }); + return findings; + } + let trajectory; + try { + trajectory = await readNativeTrajectory(changeDir, run.trajectoryRef); + if (trajectory.length === 0 || trajectory.some( + (event, index) => !event || typeof event !== "object" || event.sequence !== index + 1 || event.runId !== run.runId || typeof event.type !== "string" || !event.data || typeof event.data !== "object" || Array.isArray(event.data) + )) { + throw new Error("trajectory events are missing or inconsistent"); + } + } catch (error) { + findings.push({ + code: "trajectory-invalid", + message: `Native trajectory is invalid: ${error.message}`, + path: trajectoryFile2 + }); + return findings; + } + const checkpointFile = runPath(changeDir, run.checkpointRef); + try { + const checkpoint = await readNativeCheckpoint(changeDir, run.checkpointRef); + if (!checkpoint) { + findings.push({ + code: "checkpoint-missing", + message: "Native Run checkpoint is missing", + path: checkpointFile + }); + } else if (checkpoint.runId !== run.runId || checkpoint.stateVersion !== run.iteration || checkpoint.trajectoryOffset !== trajectory.length) { + findings.push({ + code: "checkpoint-mismatch", + message: "Native Run checkpoint does not match Run state and trajectory", + path: checkpointFile + }); + } + } catch (error) { + findings.push({ + code: "checkpoint-invalid", + message: `Native Run checkpoint is invalid: ${error.message}`, + path: checkpointFile + }); + } + return findings; +} + +// domains/comet-native/native-continuation.ts +var REPAIR_CODES = /^(?:run-|trajectory-|checkpoint-(?:missing|mismatch|invalid|progress-invalid)|transition-(?:incomplete|invalid))/u; +function requiredPhaseInputs(state) { + if (state.phase === "shape") { + return ["summary", "shared-understanding-confirmation"]; + } + if (state.phase === "build") { + return state.approval === "confirmed" ? ["summary", "artifact-or-no-code-reason"] : ["summary", "artifact-or-no-code-reason", "shared-understanding-confirmation"]; + } + if (state.phase === "verify") return ["summary", "verification-result", "verification-report"]; + return []; +} +function nativeContinuation(options) { + const findings = options.findings ?? []; + const actionableFindings = findings.filter( + (finding) => !isNativeWorkspaceAdvisoryCode(finding.code) + ); + const decision = actionableFindings.find((finding) => finding.requiresUserDecision); + const repair = actionableFindings.find( + (finding) => finding.repairCommand !== null || REPAIR_CODES.test(finding.code) + ); + const stagnationStop = actionableFindings.find( + (finding) => finding.code === "repair-stagnation-stop" + ); + const requiredInputs = [ + ...new Set(actionableFindings.map((finding) => finding.requiredAction)) + ].sort(); + if (options.done) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "done", + action: "none", + command: null, + requiresUserDecision: false, + requiredInputs: [] + }; + } + if (decision) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "await-user", + action: "work-phase", + command: null, + requiresUserDecision: true, + requiredInputs + }; + } + if (stagnationStop) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "work-phase", + command: null, + requiresUserDecision: false, + requiredInputs: ["implementation-progress-or-repair-override"] + }; + } + if (repair) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "repair", + command: repair.repairCommand, + requiresUserDecision: false, + requiredInputs + }; + } + if (options.evidenceRetreat) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "advance-phase", + command: `comet native next ${options.state.name} --summary ""`, + requiresUserDecision: false, + requiredInputs: ["summary"] + }; + } + if (actionableFindings.length > 0) { + if (options.state.phase === "archive") { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "none", + command: null, + requiresUserDecision: false, + requiredInputs + }; + } + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "work-phase", + command: null, + requiresUserDecision: false, + requiredInputs + }; + } + if (options.state.phase === "archive") { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: options.archiveReady ? "continue" : "blocked", + action: options.archiveReady ? "archive" : "none", + command: options.archiveReady ? `comet native archive ${options.state.name} --dry-run` : null, + requiresUserDecision: false, + requiredInputs: options.archiveReady ? [] : ["archive-readiness"] + }; + } + const confirmationSuffix = options.state.phase === "shape" || options.state.phase === "build" && options.state.approval !== "confirmed" ? " --confirmed" : ""; + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "advance-phase", + command: `comet native next ${options.state.name} --summary ""${confirmationSuffix}`, + requiresUserDecision: false, + requiredInputs: requiredPhaseInputs(options.state) + }; +} + +// domains/comet-native/native-findings.ts +import path36 from "path"; +var FINDING_SUMMARY_CODE_BUDGET = 8; +var EXACT_METADATA = { + "brief-blocking-question": { + severity: "error", + requiredAction: "answer-blocking-question", + retry: "next", + repair: "none" + }, + "shape-confirmation-required": { + severity: "error", + requiredAction: "confirm-shared-understanding", + retry: "next", + repair: "none" + }, + "approval-confirmation-required": { + severity: "error", + requiredAction: "confirm-shared-understanding", + retry: "next", + repair: "none" + }, + "transition-incomplete": { + severity: "error", + requiredAction: "recover-transition", + retry: "status", + repair: "doctor" + }, + "trajectory-tail-incomplete": { + severity: "error", + requiredAction: "repair-trajectory-tail", + retry: "status", + repair: "doctor" + }, + "checkpoint-progress-invalid": { + severity: "error", + requiredAction: "manually-isolate-invalid-checkpoint", + retry: "none", + repair: "none" + }, + "checkpoint-progress-incomplete": { + severity: "error", + requiredAction: "recover-progress-checkpoint", + retry: "status", + repair: "doctor" + }, + "checkpoint-manifest-invalid": { + severity: "error", + requiredAction: "record-checkpoint-again", + retry: "status", + repair: "none" + }, + "verification-scope-partial": { + severity: "error", + requiredAction: "confirm-partial-verification-scope", + retry: "next", + repair: "none" + }, + "native-change-conflict": { + severity: "error", + requiredAction: "resolve-native-change-conflict", + retry: "status", + repair: "none" + }, + "native-change-overlap": { + severity: "error", + requiredAction: "inspect-native-change-overlap", + retry: "status", + repair: "none" + }, + "contract-changed-after-approval": { + severity: "error", + requiredAction: "re-confirm-contract", + retry: "next", + repair: "none" + }, + "baseline-snapshot-incomplete": { + severity: "error", + requiredAction: "resolve-native-baseline", + retry: "none", + repair: "none" + }, + "baseline-snapshot-missing": { + severity: "error", + requiredAction: "resolve-native-baseline", + retry: "none", + repair: "none" + }, + "workspace-inspection-unavailable": { + severity: "info", + requiredAction: "migrate-workspace-identity", + retry: "status", + repair: "doctor" + }, + "repair-stagnation-warning": { + severity: "warning", + requiredAction: "change-repair-approach", + retry: "next", + repair: "none" + }, + "repair-stagnation-stop": { + severity: "error", + requiredAction: "make-progress-or-explicitly-override-repair", + retry: "none", + repair: "none" + }, + "repair-iteration-limit": { + severity: "error", + requiredAction: "change-implementation-before-starting-a-new-repair-episode", + retry: "next", + repair: "none" + }, + "repair-override-exhausted": { + severity: "error", + requiredAction: "review-repeated-failure-after-override", + retry: "none", + repair: "none" + } +}; +function inferredMetadata(code) { + const exact = EXACT_METADATA[code]; + if (exact) return exact; + if (/^(?:run-|trajectory-|checkpoint-(?:missing|mismatch|invalid)|transition-invalid)/u.test(code)) { + return { + severity: "error", + requiredAction: "isolate-or-restore-native-runtime-from-a-trusted-copy", + retry: "none", + repair: "none" + }; + } + if (code.startsWith("brief-")) { + return { + severity: "error", + requiredAction: "complete-brief", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("spec-")) { + return { + severity: "error", + requiredAction: "resolve-spec-state", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("verification-")) { + return { + severity: "error", + requiredAction: "complete-verification-evidence", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("build-")) { + return { + severity: "error", + requiredAction: "record-build-evidence", + retry: "next", + repair: "none" + }; + } + if (isNativeWorkspaceAdvisoryCode(code)) { + return { + severity: code === "workspace-inspection-unavailable" ? "info" : "warning", + requiredAction: "inspect-workspace-advisory", + retry: "status", + repair: "none" + }; + } + return { + severity: "error", + requiredAction: "resolve-finding", + retry: "status", + repair: "none" + }; +} +function projectRelativePath3(paths, state, finding) { + if (!finding.path) return null; + let target; + if (path36.isAbsolute(finding.path)) { + target = path36.resolve(finding.path); + } else if (/^(?:brief-|verification-|spec-source)/u.test(finding.code)) { + target = path36.resolve(nativeChangeDir(paths, state.name), ...finding.path.split(/[\\/]/u)); + } else { + target = path36.resolve(paths.projectRoot, ...finding.path.split(/[\\/]/u)); + } + if (!isInsidePath(paths.projectRoot, target)) return null; + const relative = path36.relative(paths.projectRoot, target).replaceAll("\\", "/"); + return relative === "" ? "." : relative; +} +function retryCommand(retry, state, code) { + if (retry === "next") { + return `comet native next ${state.name} --summary ""${code === "contract-changed-after-approval" || code === "shape-confirmation-required" || code === "approval-confirmation-required" ? " --confirmed" : ""}`; + } + if (retry === "status") return `comet native status ${state.name} --details`; + return null; +} +function structureNativeFindings(options) { + return options.findings.map((finding) => { + const metadata = inferredMetadata(finding.code); + return { + code: finding.code, + message: finding.message, + severity: metadata.severity, + path: projectRelativePath3(options.paths, options.state, finding), + requiredAction: metadata.requiredAction, + retryCommand: retryCommand(metadata.retry, options.state, finding.code), + repairCommand: metadata.repair === "doctor" ? `comet native doctor ${options.state.name} --repair${finding.code.startsWith("transition-") ? " --strategy continue" : ""}` : null, + // This is intentionally code-based, not severity-based. Model-actionable + // missing data must never be presented as a user decision. + requiresUserDecision: finding.code === "brief-blocking-question" || finding.code === "shape-confirmation-required" || finding.code === "approval-confirmation-required" || finding.code === "contract-changed-after-approval" || finding.code === "verification-scope-partial" || finding.code === "repair-iteration-limit" || finding.code === "repair-override-exhausted" + }; + }).sort((left, right) => { + const severityRank = { error: 0, warning: 1, info: 2 }; + return severityRank[left.severity] - severityRank[right.severity] || left.code.localeCompare(right.code) || (left.path ?? "").localeCompare(right.path ?? "") || left.message.localeCompare(right.message); + }); +} +function summarizeNativeFindings(findings) { + const codes = [...new Set(findings.map((finding) => finding.code))]; + return { + total: findings.length, + errors: findings.filter((finding) => finding.severity === "error").length, + warnings: findings.filter((finding) => finding.severity === "warning").length, + info: findings.filter((finding) => finding.severity === "info").length, + requiresUserDecision: findings.some((finding) => finding.requiresUserDecision), + codes: codes.slice(0, FINDING_SUMMARY_CODE_BUDGET), + truncated: codes.length > FINDING_SUMMARY_CODE_BUDGET + }; +} + +// domains/comet-native/native-resume-view.ts +import path37 from "path"; +var COMPACT_TEXT_BUDGET = 240; +var COMPACT_REASON_CODE_BUDGET = 8; +var NATIVE_INSPECTION_REASON_DETAIL_BUDGET = 50; +function compactText(value) { + const characters = Array.from(value); + return characters.length <= COMPACT_TEXT_BUDGET ? value : `${characters.slice(0, COMPACT_TEXT_BUDGET - 1).join("")}…`; +} +function reasonCode(reason) { + const separator = reason.indexOf(":"); + return separator < 0 ? reason : reason.slice(0, separator); +} +function inspectionViews(reasons) { + const codes = [...new Set(reasons.map(reasonCode))]; + const inspection = { + freshness: reasons.length === 0 || reasons.length === 1 && reasons[0] === "no-checkpoint" ? "fresh" : "stale", + codes: codes.slice(0, COMPACT_REASON_CODE_BUDGET), + reasonCount: reasons.length, + codesTruncated: codes.length > COMPACT_REASON_CODE_BUDGET + }; + return { + inspection, + inspectionDetails: { + ...inspection, + reasons: reasons.slice(0, NATIVE_INSPECTION_REASON_DETAIL_BUDGET), + reasonsTruncated: reasons.length > NATIVE_INSPECTION_REASON_DETAIL_BUDGET + } + }; +} +async function buildNativeResumeView(options) { + let pendingFinding = null; + try { + const pending = await readNativeCheckpointJournal(options.paths, options.state.name); + if (pending) { + pendingFinding = { + code: "checkpoint-progress-incomplete", + message: `Native progress checkpoint ${pending.id} requires deterministic recovery`, + path: nativeCheckpointJournalFile(options.paths, options.state.name) + }; + } + } catch (error) { + pendingFinding = { + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint journal is invalid: ${error.message}. Automatic repair is unavailable; inspect and move the invalid checkpoint journal aside before retrying`, + path: nativeCheckpointJournalFile(options.paths, options.state.name) + }; + } + const inspected = await inspectNativeCheckpointFreshness({ + paths: options.paths, + name: options.state.name, + stateRevision: options.state.revision + }); + const allReasons = pendingFinding ? [pendingFinding.code, ...inspected.reasons] : inspected.reasons; + const views = inspectionViews(allReasons); + if (!inspected.checkpoint) { + return { + inspection: views.inspection, + inspectionDetails: views.inspectionDetails, + checkpoint: null, + checkpointDetails: null, + findings: pendingFinding ? [pendingFinding, ...inspected.findings] : inspected.findings, + maxCheckpointArtifacts: NATIVE_CHECKPOINT_LIMITS.maxArtifacts + }; + } + const compact = { + id: inspected.checkpoint.id, + createdAt: inspected.checkpoint.createdAt, + phase: inspected.checkpoint.phase, + stateRevision: inspected.checkpoint.stateRevision, + summary: compactText(inspected.checkpoint.summary), + nextAction: compactText(inspected.checkpoint.nextAction), + artifactCount: inspected.checkpoint.artifactCount + }; + const details = inspected.manifest ? { + ...compact, + summary: inspected.checkpoint.summary, + nextAction: inspected.checkpoint.nextAction, + manifestHash: inspected.checkpoint.manifestHash, + manifestRef: path37.relative( + options.paths.projectRoot, + path37.join( + nativeChangeDir(options.paths, options.state.name), + ...inspected.checkpoint.manifestRef.split("/") + ) + ).replaceAll("\\", "/"), + artifacts: inspected.manifest.artifacts, + totalBytes: inspected.manifest.totalBytes + } : null; + return { + inspection: views.inspection, + inspectionDetails: views.inspectionDetails, + checkpoint: compact, + checkpointDetails: details, + findings: pendingFinding ? [pendingFinding, ...inspected.findings] : inspected.findings, + maxCheckpointArtifacts: NATIVE_CHECKPOINT_LIMITS.maxArtifacts + }; +} + +// domains/comet-native/native-repair-integration.ts +function assertRepairableBuildState(state) { + if (state.phase !== "build" || state.verification_result !== "fail" || !state.implementation_scope || !state.verification_evidence) { + throw new Error("Native repair guard requires a failed Verify-to-Build state"); + } +} +async function readNativeCommittedRepairTrajectory(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const run = await readNativeRunState(changeDir); + if (!run || !state.run_id || run.runId !== state.run_id) { + throw new Error("Native repair history Run state is missing or mismatched"); + } + const checkpoint = await readNativeCheckpoint(changeDir, run.checkpointRef); + if (!checkpoint || checkpoint.runId !== run.runId || checkpoint.stateVersion !== run.iteration) { + throw new Error("Native repair history checkpoint is missing or mismatched"); + } + return { + trajectory: await readNativeTrajectory(changeDir, run.trajectoryRef), + committedTrajectoryOffset: checkpoint.trajectoryOffset, + runId: run.runId + }; +} +async function inspectNativeRepairHistory(paths, state) { + const committed = await readNativeCommittedRepairTrajectory(paths, state); + return { + committed, + latest: inspectLatestNativeRepairProjection(committed), + history: rebuildNativeRepairHistory(committed) + }; +} +function decisionFromInspection(inspection) { + const latest = inspection.latest; + if (!latest) return null; + const failures = inspection.history.filter((entry2) => entry2.kind === "failure"); + let consecutiveFailures = 0; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== latest.signatureHash) break; + consecutiveFailures += 1; + } + const overrideAccepted = latest.overrideSummaryHash !== null; + const overrideAlreadyUsed = inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ); + return { + disposition: latest.disposition, + reasonCode: overrideAccepted ? "override-accepted" : overrideAlreadyUsed && latest.disposition === "manual-stop" ? "override-already-used" : latest.disposition === "hard-stop" ? "repair-iteration-limit" : latest.disposition === "manual-stop" ? "repeated-failure-stop" : latest.disposition === "warn" ? "repeated-failure-warning" : "new-failure-signature", + signatureHash: latest.signatureHash, + consecutiveFailures, + totalRepairFailures: failures.length, + remainingIterations: Math.max( + 0, + NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations - failures.length + ), + overrideAccepted + }; +} +async function inspectLatestNativeRepairDecision(paths, state) { + return decisionFromInspection(await inspectNativeRepairHistory(paths, state)); +} +function projectNativeRepairDecision(result2) { + return { + disposition: result2.decision.disposition, + reasonCode: result2.decision.reasonCode, + signatureHash: result2.decision.signature.signatureHash, + consecutiveFailures: result2.decision.consecutiveFailures, + totalRepairFailures: result2.decision.totalRepairFailures, + remainingIterations: result2.decision.remainingIterations, + overrideAccepted: result2.decision.overrideAccepted + }; +} +async function inspectNativeRepairFailureForTransition(options) { + if (!options.state.implementation_scope) { + throw new Error("Native repair failure has no implementation scope"); + } + const [committed, implementationScope] = await Promise.all([ + readNativeCommittedRepairTrajectory(options.paths, options.state), + readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ) + ]); + return inspectNativeRepairFailure({ + ...committed, + envelope: options.envelope, + implementationScope, + ...options.categories ? { categories: options.categories } : {}, + ...options.failedCheckIds ? { failedCheckIds: options.failedCheckIds } : {} + }); +} +async function inspectNativeRepairBuildGuard(options) { + const inspection = await inspectNativeRepairHistory(options.paths, options.state); + const latest = inspection.latest; + if (!latest || latest.disposition !== "manual-stop" && latest.disposition !== "hard-stop") { + if (options.override) { + throw new Error("Native repair override requires the latest manual stop"); + } + return { disposition: "proceed", eventProjection: null }; + } + const overrideRecorded = inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ); + const activeFailedRepair = options.state.phase === "build" && options.state.verification_result === "fail" && options.state.implementation_scope !== null && options.state.verification_evidence !== null; + if (!activeFailedRepair) { + if (options.override) { + throw new Error("Native repair override requires an active failed Verify-to-Build state"); + } + return { disposition: "proceed", eventProjection: null }; + } + assertRepairableBuildState(options.state); + const [previousEnvelope, previousImplementationScope] = await Promise.all([ + readNativeVerificationEvidence( + options.paths, + options.state.name, + options.state.verification_evidence + ), + readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ) + ]); + if (previousImplementationScope.scope.scopeHash !== previousEnvelope.implementationScopeHash) { + throw new Error("Native repair verification evidence does not match its implementation scope"); + } + if (nativeRepairScopeHash(options.currentImplementationScope) !== nativeRepairScopeHash(previousImplementationScope)) { + if (options.override) { + throw new Error("Native repair override is not valid after implementation scope progress"); + } + return { disposition: "proceed", eventProjection: null }; + } + if (latest.disposition === "hard-stop") { + if (options.override) { + throw new Error( + "Native repair hard stop cannot be overridden without implementation progress" + ); + } + return { + disposition: "hard-stop", + signatureHash: latest.signatureHash, + overrideRecorded + }; + } + if (overrideRecorded) { + return { + disposition: "hard-stop", + signatureHash: latest.signatureHash, + overrideRecorded + }; + } + if (!options.override) { + return { + disposition: "manual-stop", + signatureHash: latest.signatureHash, + overrideRecorded: false + }; + } + return { + disposition: "proceed", + eventProjection: acceptLatestNativeRepairOverride({ + ...inspection.committed, + override: options.override + }).eventProjection + }; +} +async function inspectNativeRepairStatus(paths, state) { + if (state.phase !== "build" || state.verification_result !== "fail") return null; + const inspection = await inspectNativeRepairHistory(paths, state); + const latest = inspection.latest; + if (!latest || latest.disposition !== "manual-stop" && latest.disposition !== "hard-stop") { + return null; + } + return { + disposition: latest.disposition, + signatureHash: latest.signatureHash, + overrideRecorded: inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ) + }; +} + +// domains/comet-native/native-diagnostics.ts +var NATIVE_STATUS_CURSOR_PATTERN = /^native-status-v1\.([a-f0-9]{64})\.([0-9a-z]+)\.([a-f0-9]{64})$/u; +var NATIVE_STATUS_PAGE_LIMITS = Object.freeze({ + maxItems: 24, + maxChanges: 4096, + maxSerializedBytes: 512 * 1024 +}); +async function selectedName(paths) { + try { + return (await readNativeSelectionRecord(paths))?.change ?? null; + } catch { + return null; + } +} +function nativeNextCommand(state, archiveReady, evidenceRetreat = false, _clarificationMode) { + if (state.phase === "archive") { + return archiveReady ? `comet native archive ${state.name} --dry-run` : evidenceRetreat ? `comet native next ${state.name} --summary ""` : null; + } + return `comet native next ${state.name} --summary ""${state.phase === "shape" || state.phase === "build" && state.approval !== "confirmed" ? " --confirmed" : ""}`; +} +async function statusFindings(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const findings = [ + ...(await validateNativeBrief(changeDir, state.brief)).findings, + ...(await validateNativeSpecChanges(paths, state)).findings, + ...await inspectNativeRunConsistency(paths, state) + ]; + if (state.phase === "shape" || state.phase === "build") { + try { + const capturedBaseline = await readNativeBaselineManifest(paths, state.name); + if (capturedBaseline === null) { + findings.push({ + code: "baseline-snapshot-missing", + message: "Native baseline is missing; restore a trusted baseline before advancing" + }); + } else { + const baseline = await filterNativeContentSnapshotToProjectScope(paths, capturedBaseline); + if (!baseline.complete) { + findings.push({ + code: "baseline-snapshot-incomplete", + message: `Native baseline is incomplete within the project-owned scope (${baseline.omittedCount} omitted entries); resolve the omissions before advancing` + }); + } + } + } catch (error) { + findings.push({ + code: "baseline-snapshot-invalid", + message: `Native baseline could not be inspected safely: ${error.message}` + }); + } + } + if (state.phase === "build") { + try { + const current = await collectNativeContractFiles({ + changeDir, + briefRef: state.brief, + specChanges: state.spec_changes + }); + if ((state.approved_contract_hash ?? null) !== current.contract.contractHash) { + findings.push({ + code: "contract-changed-after-approval", + message: state.approval === null || state.approved_contract_hash === null || state.approved_contract_hash === void 0 ? "Native approval is not bound to a contract hash; re-confirm the current contract" : "Native contract changed after approval; re-confirm the current contract" + }); + } + } catch (error) { + findings.push({ + code: "contract-inspection-invalid", + message: `Native approved contract could not be inspected safely: ${error.message}` + }); + } + } + try { + if (await inspectPendingNativeTransition(paths, state.name)) { + findings.unshift({ + code: "transition-incomplete", + message: "Native phase transition recovery is pending" + }); + } + } catch (error) { + findings.unshift({ + code: "transition-invalid", + message: `Native transition journal is invalid: ${error.message}` + }); + } + if (state.verification_report) { + findings.push( + ...(await validateNativeVerification(changeDir, state.verification_report)).findings + ); + } else if (state.phase === "verify" || state.phase === "archive" || state.verification_result === "pass") { + findings.push({ + code: "verification-report-missing", + message: "Native change has no verification report" + }); + } + return findings; +} +async function inspectNativeStatus(paths, name, options) { + const selected = await selectedName(paths) === name; + let state; + try { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required" && inspection.state) { + return { + name, + phase: inspection.state.phase, + revision: "revision" in inspection.state ? inspection.state.revision : null, + approval: inspection.state.approval, + verificationResult: inspection.state.verification_result, + specChanges: inspection.state.spec_changes.length, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["migration-required"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + schema: inspection.schema, + migrationRequired: true, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + error: inspection.message + }; + } + if (inspection.status !== "current" || !inspection.state) { + return { + name, + phase: "invalid", + revision: null, + approval: null, + verificationResult: "pending", + specChanges: 0, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["runtime-incompatible"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + error: inspection.message ?? `Native change ${name} is incompatible` + }; + } + state = inspection.state; + } catch (error) { + return { + name, + phase: "invalid", + revision: null, + approval: null, + verificationResult: "pending", + specChanges: 0, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["change-invalid"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + error: error.message + }; + } + const resume = await buildNativeResumeView({ paths, state }); + let acceptancePage; + if (options?.details && (state.phase === "verify" || state.phase === "archive")) { + try { + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(paths, state.name), + briefRef: state.brief, + specChanges: state.spec_changes + }); + acceptancePage = projectNativeAcceptancePage({ + criteria: contract.contract.acceptance, + acceptanceHash: contract.contract.acceptanceHash, + ...options.acceptanceCursor ? { cursor: options.acceptanceCursor } : {} + }); + } catch (error) { + if (options.acceptanceCursor) throw error; + acceptancePage = void 0; + } + } + const conflictFindings = []; + try { + const conflicts = await inspectNativeChangeConflicts(paths, state.name); + conflictFindings.push( + ...conflicts.findingCodes.map((code) => ({ + code, + message: `Native change overlap is visible in the current root: ${code}` + })) + ); + } catch { + conflictFindings.push({ + code: "native-conflict-inspection-invalid", + message: "Native change overlap could not be recomputed safely" + }); + } + const workspaceFindings = []; + try { + const identity = await readNativeWorkspaceIdentity(paths, state.name); + if (identity) { + const workspace = await inspectNativeWorkspaceAdvisory({ + paths, + identity + }); + workspaceFindings.push( + ...workspace.findingCodes.map((code) => ({ + code, + message: `Native workspace advisory changed: ${code} (${workspace.driftComponents.join(", ") || "no-component"})` + })) + ); + } + } catch { + workspaceFindings.push({ + code: "workspace-inspection-unavailable", + message: "Native workspace advisory could not be recomputed safely" + }); + } + const verifyScopeFindings = []; + let verifyEvidenceRetreat = false; + if (state.phase === "verify") { + const freshness = await inspectNativeImplementationScopeFreshness({ paths, state }); + verifyEvidenceRetreat = freshness.freshness !== "fresh"; + verifyScopeFindings.push( + ...freshness.findingCodes.map((code) => ({ + code, + message: `Native Verify implementation scope is not current: ${code}` + })) + ); + } + let repair = null; + const repairFindings = []; + if (state.phase === "build" && state.verification_result === "fail") { + try { + repair = await inspectNativeRepairStatus(paths, state); + if (repair) { + const code = repair.disposition === "hard-stop" ? "repair-iteration-limit" : repair.overrideRecorded ? "repair-override-exhausted" : "repair-stagnation-stop"; + repairFindings.push({ + code, + message: `Native repair is stopped at failure signature: ${repair.signatureHash}` + }); + } + } catch { + repairFindings.push({ + code: "trajectory-invalid", + message: "Native repair history could not be reconstructed safely" + }); + } + } + let archivePreflight = null; + const archiveFindings = []; + if (state.phase === "archive") { + try { + archivePreflight = await inspectNativeArchivePreflight({ paths, name: state.name }); + archiveFindings.push( + ...archivePreflight.findingCodes.map((code) => ({ + code, + message: `Native Archive is blocked: ${code}` + })) + ); + } catch { + archiveFindings.push({ + code: "archive-preflight-invalid", + message: "Native Archive preflight could not be recomputed safely" + }); + } + } + const rawFindings = [ + ...await statusFindings(paths, state), + ...resume.findings, + ...conflictFindings, + ...workspaceFindings, + ...verifyScopeFindings, + ...repairFindings, + ...archiveFindings + ].filter( + (finding, index, values) => values.findIndex( + (candidate) => candidate.code === finding.code && candidate.path === finding.path + ) === index + ); + const findings = structureNativeFindings({ paths, state, findings: rawFindings }); + const archiveBlockingFindings = findings.filter( + (finding) => !isNativeWorkspaceAdvisoryCode(finding.code) + ); + const archiveReady = state.phase === "archive" && archivePreflight?.ready === true && archiveBlockingFindings.length === 0; + const evidenceRetreat = verifyEvidenceRetreat || state.phase === "archive" && (archivePreflight?.findingCodes ?? []).some( + (code) => (/* @__PURE__ */ new Set([ + "verification-evidence-stale", + "verification-evidence-invalid", + "verification-evidence-missing", + "verification-contract-stale", + "verification-implementation-stale", + "verification-report-stale", + "verification-state-mismatch" + ])).has(code) + ); + const mutationBlocked = findings.some( + (finding) => finding.code === "trajectory-tail-incomplete" || finding.code === "trajectory-invalid" + ); + const repairBlocked = repair !== null; + const firstErrorFinding = findings.find((finding) => finding.severity === "error"); + return { + name: state.name, + phase: state.phase, + revision: state.revision, + approval: state.approval, + verificationResult: state.verification_result, + specChanges: state.spec_changes.length, + selected, + nextCommand: mutationBlocked || repairBlocked ? null : nativeNextCommand(state, archiveReady, evidenceRetreat, options?.clarificationMode), + archiveReady, + inspection: resume.inspection, + findingSummary: summarizeNativeFindings(findings), + detailsCommand: `comet native status ${state.name} --details`, + checkpoint: resume.checkpoint, + continuation: nativeContinuation({ + state, + findings, + archiveReady, + evidenceRetreat, + clarificationMode: options?.clarificationMode + }), + repair, + ...options?.details ? { + ...acceptancePage ? { acceptancePage } : {}, + findings: findings.slice(0, 50), + inspectionDetails: resume.inspectionDetails, + checkpointDetails: resume.checkpointDetails, + budgets: { + maxFindings: 50, + maxInspectionReasons: NATIVE_INSPECTION_REASON_DETAIL_BUDGET, + maxCheckpointArtifacts: resume.maxCheckpointArtifacts, + findingsTruncated: findings.length > 50, + inspectionReasonsTruncated: resume.inspectionDetails.reasonsTruncated, + checkpointArtifactsTruncated: false + } + } : {}, + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + ...firstErrorFinding ? { error: firstErrorFinding.message } : {} + }; +} +async function boundedNativeChangeNames(paths) { + let guard; + try { + guard = await captureNativeProtectedDirectoryGuard({ + root: paths.nativeRoot, + directory: paths.changesDir, + label: "Native status changes directory" + }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const names = []; + const directory = await fs24.opendir(paths.changesDir); + try { + for await (const entry2 of directory) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + names.push(entry2.name); + if (names.length > NATIVE_STATUS_PAGE_LIMITS.maxChanges) { + throw new Error( + `Native status exceeds ${NATIVE_STATUS_PAGE_LIMITS.maxChanges} visible changes` + ); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + await guard.verify(); + return names.sort(); +} +function nativeStatusCursor(namesHash, offset) { + const encodedOffset = offset.toString(36); + const integrity = canonicalHash("comet.native.status-cursor.v1", { namesHash, offset }); + return `native-status-v1.${namesHash}.${encodedOffset}.${integrity}`; +} +function nativeStatusOffset(options) { + if (options.cursor === void 0 || options.cursor === null) return 0; + const match = NATIVE_STATUS_CURSOR_PATTERN.exec(options.cursor); + if (!match) throw new Error("Native status cursor is invalid"); + if (match[1] !== options.namesHash) throw new Error("Native status cursor is stale"); + const offset = Number.parseInt(match[2], 36); + if (!Number.isSafeInteger(offset) || offset <= 0 || offset >= options.total || offset.toString(36) !== match[2]) { + throw new Error("Native status cursor offset is invalid"); + } + const expected = canonicalHash("comet.native.status-cursor.v1", { + namesHash: options.namesHash, + offset + }); + if (match[3] !== expected) throw new Error("Native status cursor integrity check failed"); + return offset; +} +async function listNativeStatusPage(paths, options) { + const names = await boundedNativeChangeNames(paths); + const namesHash = canonicalHash("comet.native.status-names.v1", names); + const offset = nativeStatusOffset({ + namesHash, + total: names.length, + cursor: options?.cursor + }); + const candidates = await Promise.all( + names.slice(offset, offset + NATIVE_STATUS_PAGE_LIMITS.maxItems).map( + (name) => inspectNativeStatus(paths, name, { + clarificationMode: options?.clarificationMode + }) + ) + ); + const items = []; + for (const candidate of candidates) { + const trialItems = [...items, candidate]; + const nextOffset2 = offset + trialItems.length; + const trial = { + schema: "comet.native.status-page.v1", + total: names.length, + offset, + items: trialItems, + nextCursor: nextOffset2 < names.length ? nativeStatusCursor(namesHash, nextOffset2) : null, + limits: { ...NATIVE_STATUS_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(trial), "utf8") > NATIVE_STATUS_PAGE_LIMITS.maxSerializedBytes) { + if (items.length === 0) { + throw new Error("Native status item exceeds its page serialization budget"); + } + break; + } + items.push(candidate); + } + const nextOffset = offset + items.length; + return { + schema: "comet.native.status-page.v1", + total: names.length, + offset, + items, + nextCursor: nextOffset < names.length ? nativeStatusCursor(namesHash, nextOffset) : null, + limits: { ...NATIVE_STATUS_PAGE_LIMITS } + }; +} +async function listNativeStatus(paths, options) { + return (await listNativeStatusPage(paths, { + clarificationMode: options?.clarificationMode + })).items; +} + +// domains/comet-native/native-doctor.ts +import { promises as fs28 } from "fs"; +import path41 from "path"; + +// domains/comet-native/native-evidence-retention.ts +import { createHash as createHash13, randomUUID as randomUUID8 } from "node:crypto"; +import { constants as fsConstants5, promises as fs26 } from "node:fs"; +import path39 from "node:path"; + +// domains/comet-native/native-schema-migration.ts +var import_yaml4 = __toESM(require_dist(), 1); +import { randomUUID as randomUUID7 } from "crypto"; +import { promises as fs25 } from "fs"; +import path38 from "path"; +import { isDeepStrictEqual as isDeepStrictEqual3 } from "util"; +var HASH_PATTERN14 = /^[a-f0-9]{64}$/u; +var MIGRATION_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "change", + "fromSchema", + "toSchema", + "sourceHash", + "targetHash", + "createdAt", + "nextState", + "transition", + "transitionSupersede", + "runRetreat" +]); +var MIGRATION_TRANSITION_KEYS = /* @__PURE__ */ new Set(["sourceHash", "targetHash", "nextJournal"]); +var MIGRATION_TRANSITION_SUPERSEDE_KEYS = /* @__PURE__ */ new Set([ + "sourceHash", + "transitionId", + "previousRun", + "nextRun", + "evidenceHash", + "eventData" +]); +var MIGRATION_SUPERSEDE_EVENT_KEYS = /* @__PURE__ */ new Set([ + "fromSchema", + "toSchema", + "previousPhase", + "nextPhase", + "reason", + "supersededTransitionId" +]); +var MIGRATION_RUN_RETREAT_KEYS = /* @__PURE__ */ new Set(["previousRun", "nextRun", "evidenceHash", "eventData"]); +function rejectUnknownFields(value, keys, label) { + const unknown = Object.keys(value).find((key) => !keys.has(key)); + if (unknown) throw new Error(`${label} contains unknown field: ${unknown}`); +} +function transitionContent(journal) { + return JSON.stringify(journal, null, 2) + "\n"; +} +function migrationStateDocument(state) { + return state.schema === NATIVE_V2_CHANGE_SCHEMA ? nativeV2ChangeDocument(state) : nativeChangeDocument(state); +} +function upgradeV1StateToV2(state, revision) { + return { + ...state, + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision + }; +} +function upgradeV1TransitionToV2(journal) { + return { + ...journal, + schema: NATIVE_V2_TRANSITION_SCHEMA, + minimum_runtime_version: 2, + revision: 1, + previousState: upgradeV1StateToV2(journal.previousState, 1), + nextState: upgradeV1StateToV2(journal.nextState, 2) + }; +} +function upgradeV2StateToV3(state, options) { + const retreat = options?.retreatEvidencePhase === true && (state.phase === "verify" || state.phase === "archive"); + return { + ...state, + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: state.revision + (retreat && options?.incrementRetreatRevision ? 1 : 0), + phase: retreat ? "build" : state.phase, + verification_result: retreat ? "pending" : state.verification_result, + verification_report: retreat ? null : state.verification_report, + approved_contract_hash: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: retreat ? false : state.archived + }; +} +function upgradeV2TransitionToV3(journal) { + if (journal.nextState.phase === "archive") { + throw new Error("Native v2 Archive transition must be superseded by schema migration"); + } + const specRebase = journal.previousState.phase !== "shape" && journal.nextState.phase === "build" && journal.eventData.verificationResult === null; + const evidenceHash = specRebase ? sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: journal.change, + summary: journal.eventData.summary, + specChanges: journal.nextState.spec_changes + }) + ) : journal.evidenceHash; + return { + ...journal, + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + operation: specRebase ? "spec-rebase" : "advance", + evidenceHash, + previousState: upgradeV2StateToV3(journal.previousState), + nextState: { + ...upgradeV2StateToV3(journal.nextState), + ...specRebase ? { + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + } : {} + }, + eventData: { ...journal.eventData, evidenceHash }, + nextRun: journal.previousRun === null ? { + ...journal.nextRun, + skillVersion: NATIVE_RUNTIME_PACKAGE.definition.metadata.version, + skillHash: NATIVE_RUNTIME_HASH + } : journal.nextRun + }; +} +function sameV1State(left, right) { + return JSON.stringify(left) === JSON.stringify(right); +} +function sameV2State(left, right) { + return JSON.stringify(nativeV2ChangeDocument(left)) === JSON.stringify(nativeV2ChangeDocument(right)); +} +function sameCurrentState(left, right) { + return JSON.stringify(nativeChangeDocument(left)) === JSON.stringify(nativeChangeDocument(right)); +} +function sameRunState(left, right) { + return isDeepStrictEqual3(left, right); +} +function parseTransitionSupersede(value, expectedName, nextState, migrationId) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Schema migration transition supersede plan is invalid"); + } + const record8 = value; + rejectUnknownFields( + record8, + MIGRATION_TRANSITION_SUPERSEDE_KEYS, + "Schema migration transition supersede plan" + ); + if (typeof record8.sourceHash !== "string" || !HASH_PATTERN14.test(record8.sourceHash)) { + throw new Error("Schema migration transition supersede source hash is invalid"); + } + if (typeof record8.transitionId !== "string" || record8.transitionId.length === 0) { + throw new Error("Schema migration superseded transition id is invalid"); + } + if (typeof record8.evidenceHash !== "string" || !HASH_PATTERN14.test(record8.evidenceHash)) { + throw new Error("Schema migration transition supersede evidence hash is invalid"); + } + const previousRun = parseNativeStoredRunStateValue(record8.previousRun); + const nextRun = parseNativeStoredRunStateValue(record8.nextRun); + if (previousRun.runId !== nextRun.runId || nextState.run_id !== nextRun.runId || nextState.phase !== "build" && nextState.phase !== "verify" || nextRun.currentStep !== nextState.phase || nextRun.pending !== null || nextRun.status !== "running" || nextState.verification_result !== "pending" || nextState.verification_report !== null || nextState.implementation_scope !== null || nextState.verification_evidence !== null || nextState.partial_allowance !== null) { + throw new Error("Schema migration transition supersede Run does not match target state"); + } + const retreatAllowed = previousRun.currentStep === "verify" && nextState.phase === "build" || previousRun.currentStep === "archive" && nextState.phase === "build"; + const expectedNextRun = previousRun.currentStep === nextState.phase ? previousRun : retreatAllowed ? { + ...previousRun, + currentStep: nextState.phase, + iteration: previousRun.iteration + 1, + pending: null, + status: "running" + } : null; + if (!expectedNextRun || !sameRunState(expectedNextRun, nextRun)) { + throw new Error("Schema migration transition supersede Run retreat is invalid"); + } + if (!record8.eventData || typeof record8.eventData !== "object" || Array.isArray(record8.eventData)) { + throw new Error("Schema migration transition supersede event is invalid"); + } + const eventData = record8.eventData; + const eventKeys = Object.keys(eventData); + if (eventKeys.length !== MIGRATION_SUPERSEDE_EVENT_KEYS.size || eventKeys.some((key) => !MIGRATION_SUPERSEDE_EVENT_KEYS.has(key)) || eventData.fromSchema !== NATIVE_V2_CHANGE_SCHEMA || eventData.toSchema !== NATIVE_CHANGE_SCHEMA || !["build", "verify", "archive"].includes(eventData.previousPhase) || eventData.nextPhase !== nextState.phase || eventData.reason !== (nextState.phase === "build" ? "implementation-scope-required" : "verification-evidence-required") || eventData.supersededTransitionId !== record8.transitionId) { + throw new Error("Schema migration transition supersede event semantics are invalid"); + } + if (nextState.name !== expectedName) { + throw new Error("Schema migration transition supersede change mismatch"); + } + const expectedEvidenceHash = sha256Text( + JSON.stringify({ + operation: "supersede-v2-evidence-transition", + change: expectedName, + transitionId: record8.transitionId, + migrationId, + previousPhase: eventData.previousPhase, + nextPhase: eventData.nextPhase, + reason: eventData.reason, + previousIteration: previousRun.iteration, + nextRevision: nextState.revision + }) + ); + if (record8.evidenceHash !== expectedEvidenceHash) { + throw new Error("Schema migration transition supersede evidence hash mismatch"); + } + return { + sourceHash: record8.sourceHash, + transitionId: record8.transitionId, + previousRun, + nextRun, + evidenceHash: record8.evidenceHash, + eventData + }; +} +function nativeSchemaMigrationJournalFile(paths, name) { + return path38.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); +} +function parseMigrationJournal(value, expectedName) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native schema migration journal must be an object"); + } + rejectUnknownFields( + value, + MIGRATION_JOURNAL_KEYS, + "Native schema migration journal" + ); + const journal = value; + if (journal.schema !== "comet.native.schema-migration.v1") { + throw new Error("Unsupported Native schema migration journal"); + } + if (journal.change !== expectedName) throw new Error("Schema migration change mismatch"); + const v1ToV2 = journal.fromSchema === NATIVE_LEGACY_CHANGE_SCHEMA && journal.toSchema === NATIVE_V2_CHANGE_SCHEMA; + const v2ToV3 = journal.fromSchema === NATIVE_V2_CHANGE_SCHEMA && journal.toSchema === NATIVE_CHANGE_SCHEMA; + if (!v1ToV2 && !v2ToV3) throw new Error("Schema migration route is unsupported"); + if (typeof journal.id !== "string" || journal.id.length === 0) { + throw new Error("Schema migration id is invalid"); + } + if (typeof journal.sourceHash !== "string" || !HASH_PATTERN14.test(journal.sourceHash) || typeof journal.targetHash !== "string" || !HASH_PATTERN14.test(journal.targetHash)) { + throw new Error("Schema migration hash is invalid"); + } + if (typeof journal.createdAt !== "string" || Number.isNaN(Date.parse(journal.createdAt))) { + throw new Error("Schema migration timestamp is invalid"); + } + const nextState = v1ToV2 ? parseV2NativeChangeValue(journal.nextState) : parseNativeChangeValue(journal.nextState); + if (nextState.name !== expectedName) { + throw new Error("Schema migration target state change mismatch"); + } + if (sha256Text((0, import_yaml4.stringify)(migrationStateDocument(nextState))) !== journal.targetHash) { + throw new Error("Schema migration state target hash does not match its document"); + } + let transition; + if (journal.transition !== void 0) { + if (!journal.transition || typeof journal.transition !== "object") { + throw new Error("Schema migration transition target is invalid"); + } + rejectUnknownFields( + journal.transition, + MIGRATION_TRANSITION_KEYS, + "Schema migration transition target" + ); + const transitionValue = journal.transition; + if (typeof transitionValue.sourceHash !== "string" || !HASH_PATTERN14.test(transitionValue.sourceHash) || typeof transitionValue.targetHash !== "string" || !HASH_PATTERN14.test(transitionValue.targetHash)) { + throw new Error("Schema migration transition hash is invalid"); + } + const parsedNextJournal = v1ToV2 ? parseV2NativeTransitionJournalValue(transitionValue.nextJournal, expectedName) : parseNativeTransitionJournalValue(transitionValue.nextJournal, expectedName); + if (sha256Text(JSON.stringify(transitionValue.nextJournal, null, 2) + "\n") !== transitionValue.targetHash) { + throw new Error("Schema migration transition target hash does not match its journal"); + } + const matches = v1ToV2 && nextState.schema === NATIVE_V2_CHANGE_SCHEMA ? sameV2State(nextState, parsedNextJournal.previousState) || sameV2State(nextState, parsedNextJournal.nextState) : nextState.schema === NATIVE_CHANGE_SCHEMA && (sameCurrentState( + nextState, + parsedNextJournal.previousState + ) || sameCurrentState(nextState, parsedNextJournal.nextState)); + if (!matches) throw new Error("Schema migration state/transition target mismatch"); + transition = { + sourceHash: transitionValue.sourceHash, + targetHash: transitionValue.targetHash, + nextJournal: transitionValue.nextJournal + }; + } + if (v1ToV2 && (!transition && nextState.revision !== 1 || transition && nextState.revision !== 1 && nextState.revision !== 2)) { + throw new Error("Schema migration v1 target revision is invalid"); + } + let transitionSupersede; + if (journal.transitionSupersede !== void 0) { + if (!v2ToV3 || transition) { + throw new Error("Schema migration transition supersede plan is not valid for this route"); + } + if (nextState.schema !== NATIVE_CHANGE_SCHEMA) { + throw new Error("Schema migration transition supersede target schema is invalid"); + } + transitionSupersede = parseTransitionSupersede( + journal.transitionSupersede, + expectedName, + nextState, + journal.id + ); + } + let runRetreat; + if (journal.runRetreat !== void 0) { + if (!v2ToV3 || transition || transitionSupersede) { + throw new Error("Schema migration Run retreat is not valid for this route"); + } + if (!journal.runRetreat || typeof journal.runRetreat !== "object") { + throw new Error("Schema migration Run retreat is invalid"); + } + rejectUnknownFields( + journal.runRetreat, + MIGRATION_RUN_RETREAT_KEYS, + "Schema migration Run retreat" + ); + const retreat = journal.runRetreat; + let previousRun; + let nextRun; + try { + previousRun = parseNativeStoredRunStateValue(retreat.previousRun); + nextRun = parseNativeStoredRunStateValue(retreat.nextRun); + } catch (error) { + throw new Error("Schema migration Run retreat is invalid", { cause: error }); + } + if (typeof retreat.evidenceHash !== "string" || !HASH_PATTERN14.test(retreat.evidenceHash) || !retreat.eventData || typeof retreat.eventData !== "object" || Array.isArray(retreat.eventData)) { + throw new Error("Schema migration Run retreat is invalid"); + } + if (nextState.schema !== NATIVE_CHANGE_SCHEMA || nextState.phase !== "build" || nextState.run_id !== nextRun.runId || previousRun.runId !== nextRun.runId || previousRun.currentStep !== "verify" && previousRun.currentStep !== "archive" || nextRun.currentStep !== "build" || nextRun.iteration !== previousRun.iteration + 1) { + throw new Error("Schema migration Run retreat does not match target state"); + } + runRetreat = { + previousRun, + nextRun, + evidenceHash: retreat.evidenceHash, + eventData: retreat.eventData + }; + } + if (transition && runRetreat || transitionSupersede && runRetreat) { + throw new Error("Schema migration has conflicting recovery plans"); + } + return { + schema: "comet.native.schema-migration.v1", + id: journal.id, + change: expectedName, + fromSchema: journal.fromSchema, + toSchema: journal.toSchema, + sourceHash: journal.sourceHash, + targetHash: journal.targetHash, + createdAt: journal.createdAt, + nextState, + ...transition ? { transition } : {}, + ...transitionSupersede ? { transitionSupersede } : {}, + ...runRetreat ? { runRetreat } : {} + }; +} +async function inspectPendingNativeSchemaMigration(paths, name) { + const file = nativeSchemaMigrationJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const source = await readNativeProtectedTextFile({ + root: paths.nativeRoot, + file, + maxBytes: 512 * 1024, + label: "Native schema migration journal" + }); + return parseMigrationJournal(JSON.parse(source.text), name); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function ensureMigrationBaseline(paths, name, createdAt) { + const stored = await readNativeBaselineManifest(paths, name); + const baseline = stored ? await filterNativeContentSnapshotToProjectScope(paths, stored) : await createNativeContentSnapshot(paths, { + now: new Date(createdAt), + origin: "legacy-migration" + }); + if (!baseline.complete) { + const health = inspectNativeContentSnapshotHealth(baseline); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + throw new NativeBaselineIncompleteError( + name, + baseline.omittedCount, + omittedByReason, + health.samplePaths, + health.sampleTruncated + ); + } + if (stored === null) await writeNativeBaselineManifest(paths, name, baseline); +} +async function optionalFileHash(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +function expectedSupersedeEvent(journal, sequence) { + const supersede = journal.transitionSupersede; + return { + sequence, + timestamp: journal.createdAt, + type: "state_migrated", + runId: supersede.nextRun.runId, + data: { + ...supersede.eventData, + migrationId: journal.id, + evidenceHash: supersede.evidenceHash + } + }; +} +async function inspectSupersedeEvent(paths, journal) { + const supersede = journal.transitionSupersede; + const changeDir = nativeChangeDir(paths, journal.change); + const trajectory = await readNativeTrajectory(changeDir, supersede.nextRun.trajectoryRef); + const matches = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.migrationId === journal.id); + if (matches.length > 1) { + throw new Error(`Native schema migration event ${journal.id} is duplicated`); + } + const match = matches[0]; + if (match && !isDeepStrictEqual3(match.event, expectedSupersedeEvent(journal, match.index + 1))) { + throw new Error(`Native schema migration event ${journal.id} changed`); + } + return { trajectoryLength: trajectory.length, sequence: match?.event.sequence ?? null }; +} +async function assertSupersedeSourceBeforeMutation(paths, journal, stateAtTarget) { + if (!journal.transitionSupersede) return; + const transitionFile = nativeTransitionJournalFile(paths, journal.change); + const actualHash = await optionalFileHash(transitionFile); + if (actualHash === journal.transitionSupersede.sourceHash) return; + if (actualHash !== null) { + throw new Error( + `Native superseded transition source changed for ${journal.change}: expected ${journal.transitionSupersede.sourceHash}, actual ${actualHash}` + ); + } + const run = await readNativeRunState(nativeChangeDir(paths, journal.change)); + const event = await inspectSupersedeEvent(paths, journal); + const checkpoint = await readNativeCheckpoint( + nativeChangeDir(paths, journal.change), + journal.transitionSupersede.nextRun.checkpointRef + ); + if (!stateAtTarget || !run || !sameRunState(run, journal.transitionSupersede.nextRun) || event.sequence === null || !checkpoint || checkpoint.runId !== journal.transitionSupersede.nextRun.runId || checkpoint.stateVersion !== journal.transitionSupersede.nextRun.iteration || checkpoint.trajectoryOffset !== event.sequence || checkpoint.contextHash !== null || checkpoint.artifactsHash !== sha256Text(journal.transitionSupersede.evidenceHash) || checkpoint.createdAt !== journal.createdAt) { + throw new Error( + `Native superseded transition disappeared before migration ${journal.id} was durable` + ); + } +} +async function continueTransitionSupersede(paths, journal, hooks) { + const supersede = journal.transitionSupersede; + if (!supersede) return; + const changeDir = nativeChangeDir(paths, journal.change); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) { + throw new Error(`Native schema migration Run state disappeared for ${journal.change}`); + } + if (!sameRunState(currentRun, supersede.nextRun)) { + if (!sameRunState(currentRun, supersede.previousRun)) { + throw new Error(`Native schema migration Run source changed for ${journal.change}`); + } + await writeNativeRunState(changeDir, supersede.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + if (!sameRunState(await readNativeRunState(changeDir), supersede.nextRun)) { + throw new Error(`Native schema migration Run write diverged for ${journal.change}`); + } + let event = await inspectSupersedeEvent(paths, journal); + if (event.sequence === null) { + const appended = await appendNativeTrajectoryEvent({ + changeDir, + run: supersede.nextRun, + type: "state_migrated", + data: { + ...supersede.eventData, + migrationId: journal.id, + evidenceHash: supersede.evidenceHash + }, + now: new Date(journal.createdAt) + }); + event = { trajectoryLength: appended.sequence, sequence: appended.sequence }; + } + await hooks?.afterTrajectoryWritten?.(journal); + await writeNativeCheckpoint2({ + changeDir, + run: supersede.nextRun, + trajectoryOffset: event.sequence, + evidenceHash: supersede.evidenceHash, + now: new Date(journal.createdAt) + }); + await hooks?.afterCheckpointWritten?.(journal); + const transitionFile = nativeTransitionJournalFile(paths, journal.change); + const transitionHash = await optionalFileHash(transitionFile); + if (transitionHash !== null) { + if (transitionHash !== supersede.sourceHash) { + throw new Error(`Native superseded transition changed before removal for ${journal.change}`); + } + await fs25.rm(transitionFile); + await hooks?.afterTransitionSuperseded?.(journal); + } +} +async function continueRunRetreat(paths, journal, hooks) { + if (!journal.runRetreat) return; + const changeDir = nativeChangeDir(paths, journal.change); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) { + throw new Error(`Native schema migration Run state disappeared for ${journal.change}`); + } + if (!sameRunState(currentRun, journal.runRetreat.nextRun)) { + if (!sameRunState(currentRun, journal.runRetreat.previousRun)) { + throw new Error(`Native schema migration Run source changed for ${journal.change}`); + } + await writeNativeRunState(changeDir, journal.runRetreat.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + let trajectory = await readNativeTrajectory(changeDir, journal.runRetreat.nextRun.trajectoryRef); + let event = trajectory.find( + (item) => item.type === "state_migrated" && item.data.migrationId === journal.id + ); + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir, + run: journal.runRetreat.nextRun, + type: "state_migrated", + data: { + ...journal.runRetreat.eventData, + migrationId: journal.id, + evidenceHash: journal.runRetreat.evidenceHash + }, + now: new Date(journal.createdAt) + }); + trajectory = [...trajectory, event]; + } + await hooks?.afterTrajectoryWritten?.(journal); + await writeNativeCheckpoint2({ + changeDir, + run: journal.runRetreat.nextRun, + trajectoryOffset: trajectory.length, + evidenceHash: journal.runRetreat.evidenceHash, + now: new Date(journal.createdAt) + }); + await hooks?.afterCheckpointWritten?.(journal); +} +async function continueNativeSchemaMigrationLocked(paths, name, hooks) { + const journal = await inspectPendingNativeSchemaMigration(paths, name); + if (!journal) return null; + if (await hasPendingNativeCheckpointRecovery(paths, name)) { + throw new Error( + `Native change ${name} has a pending progress checkpoint; recover it with its v2 runtime before schema migration` + ); + } + const changeFile = path38.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + const actualHash = await sha256File(changeFile); + await assertSupersedeSourceBeforeMutation(paths, journal, actualHash === journal.targetHash); + await ensureMigrationBaseline(paths, name, journal.createdAt); + if (actualHash !== journal.targetHash) { + if (actualHash !== journal.sourceHash) { + throw new Error( + `Native schema migration source changed for ${name}: expected ${journal.sourceHash}, actual ${actualHash}` + ); + } + await atomicWriteText(changeFile, (0, import_yaml4.stringify)(migrationStateDocument(journal.nextState))); + await hooks?.afterStateWritten?.(journal); + } + if (journal.transition) { + const transitionFile = nativeTransitionJournalFile(paths, name); + const actualTransitionHash = await sha256File(transitionFile); + if (actualTransitionHash !== journal.transition.targetHash) { + if (actualTransitionHash !== journal.transition.sourceHash) { + throw new Error( + `Native transition migration source changed for ${name}: expected ${journal.transition.sourceHash}, actual ${actualTransitionHash}` + ); + } + await atomicWriteJson(transitionFile, journal.transition.nextJournal); + await hooks?.afterTransitionWritten?.(journal); + } + } + await continueTransitionSupersede(paths, journal, hooks); + await continueRunRetreat(paths, journal, hooks); + await fs25.rm(nativeSchemaMigrationJournalFile(paths, name), { force: true }); + return journal.nextState; +} +async function stableEvidenceRetreat(options) { + const changeDir = nativeChangeDir(options.paths, options.state.name); + const run = await readNativeRunState(changeDir); + if (!run || run.runId !== options.state.run_id || run.currentStep !== options.state.phase || run.currentStep !== "verify" && run.currentStep !== "archive" || run.pending !== null) { + throw new Error( + `Native v2 ${options.state.phase} change ${options.state.name} has no consistent Run state for safe Build retreat` + ); + } + const nextRun = { + ...run, + currentStep: "build", + iteration: run.iteration + 1, + pending: null, + status: "running" + }; + const evidenceHash = sha256Text( + `schema-v3-evidence-retreat:${options.state.name}:${options.state.revision}:${options.migrationId}` + ); + return { + previousRun: run, + nextRun, + evidenceHash, + eventData: { + fromSchema: NATIVE_V2_CHANGE_SCHEMA, + toSchema: NATIVE_CHANGE_SCHEMA, + previousPhase: options.state.phase, + nextPhase: "build", + reason: "implementation-scope-required" + } + }; +} +async function pendingEvidenceTransitionSupersede(options) { + const { journal, state } = options; + const requiresV3Evidence = journal.previousState.phase === "build" && journal.nextState.phase === "verify" || journal.previousState.phase === "verify" && (journal.nextState.phase === "build" || journal.nextState.phase === "archive"); + if (!requiresV3Evidence) { + throw new Error("Only a pending v2 evidence-bearing transition can be superseded"); + } + const stateIsPrevious = sameV2State(state, journal.previousState); + const stateIsNext = sameV2State(state, journal.nextState); + if (!stateIsPrevious && !stateIsNext) { + throw new Error(`Native change ${state.name} does not match its v2 evidence transition`); + } + const changeDir = nativeChangeDir(options.paths, state.name); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) throw new Error(`Native v2 transition Run is missing for ${state.name}`); + const runIsPrevious = journal.previousRun !== null && sameRunState(currentRun, journal.previousRun); + const runIsNext = sameRunState(currentRun, journal.nextRun); + if (!runIsPrevious && !runIsNext || stateIsNext && !runIsNext) { + throw new Error(`Native v2 transition Run does not match its durable state for ${state.name}`); + } + const trajectory = await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef); + const collisions = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.transitionId === journal.id); + if (collisions.length > 1) { + throw new Error(`Native v2 evidence transition ${journal.id} has duplicate trajectory events`); + } + const durable = collisions[0]; + if (durable) { + const expected = { + sequence: durable.index + 1, + timestamp: journal.createdAt, + type: "state_transitioned", + runId: journal.nextRun.runId, + data: { ...journal.eventData, transitionId: journal.id } + }; + if (!isDeepStrictEqual3(durable.event, expected) || !stateIsNext || !runIsNext) { + throw new Error( + `Native v2 evidence transition ${journal.id} trajectory event does not match its journal` + ); + } + } + const nextState = { + ...upgradeV2StateToV3(state), + revision: state.revision + (state.phase === "build" ? 0 : 1), + phase: "build", + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: false + }; + const previousRun = currentRun; + const nextRun = currentRun.currentStep !== "build" ? { + ...currentRun, + currentStep: "build", + iteration: currentRun.iteration + 1, + pending: null, + status: "running" + } : currentRun; + const evidenceHash = sha256Text( + JSON.stringify({ + operation: "supersede-v2-evidence-transition", + change: state.name, + transitionId: journal.id, + migrationId: options.migrationId, + previousPhase: state.phase, + nextPhase: "build", + reason: "implementation-scope-required", + previousIteration: currentRun.iteration, + nextRevision: nextState.revision + }) + ); + return { + nextState, + plan: { + sourceHash: await sha256File(nativeTransitionJournalFile(options.paths, state.name)), + transitionId: journal.id, + previousRun, + nextRun, + evidenceHash, + eventData: { + fromSchema: NATIVE_V2_CHANGE_SCHEMA, + toSchema: NATIVE_CHANGE_SCHEMA, + previousPhase: state.phase, + nextPhase: "build", + reason: "implementation-scope-required", + supersededTransitionId: journal.id + } + } + }; +} +async function prepareNextMigration(options) { + let nextState; + let transition; + let transitionSupersede; + let runRetreat; + if (options.state.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + nextState = upgradeV1StateToV2(options.state, 1); + if (options.pendingTransition) { + if (options.pendingTransition.journal.schema !== NATIVE_LEGACY_TRANSITION_SCHEMA) { + throw new Error("Native v1 change has a transition from another schema generation"); + } + const nextJournal = upgradeV1TransitionToV2(options.pendingTransition.journal); + if (sameV1State(options.state, options.pendingTransition.journal.previousState)) { + nextState = nextJournal.previousState; + } else if (sameV1State(options.state, options.pendingTransition.journal.nextState)) { + nextState = nextJournal.nextState; + } else { + throw new Error( + `Native change ${options.name} does not match either state in its v1 transition journal` + ); + } + const transitionFile = nativeTransitionJournalFile(options.paths, options.name); + transition = { + sourceHash: await sha256File(transitionFile), + targetHash: sha256Text(transitionContent(nextJournal)), + nextJournal + }; + } + } else { + if (options.pendingTransition) { + if (options.pendingTransition.journal.schema !== NATIVE_V2_TRANSITION_SCHEMA) { + throw new Error("Native v2 change has a transition from another schema generation"); + } + const v2Journal = options.pendingTransition.journal; + const requiresV3Evidence = v2Journal.previousState.phase === "build" && v2Journal.nextState.phase === "verify" || v2Journal.previousState.phase === "verify" && (v2Journal.nextState.phase === "build" || v2Journal.nextState.phase === "archive"); + if (requiresV3Evidence) { + const supersede = await pendingEvidenceTransitionSupersede({ + paths: options.paths, + state: options.state, + journal: v2Journal, + migrationId: options.id + }); + nextState = supersede.nextState; + transitionSupersede = supersede.plan; + } else { + const nextJournal = upgradeV2TransitionToV3(v2Journal); + if (sameV2State(options.state, options.pendingTransition.journal.previousState)) { + nextState = nextJournal.previousState; + } else if (sameV2State(options.state, options.pendingTransition.journal.nextState)) { + nextState = nextJournal.nextState; + } else { + throw new Error( + `Native change ${options.name} does not match either state in its v2 transition journal` + ); + } + const transitionFile = nativeTransitionJournalFile(options.paths, options.name); + transition = { + sourceHash: await sha256File(transitionFile), + targetHash: sha256Text(transitionContent(nextJournal)), + nextJournal + }; + } + } else { + nextState = upgradeV2StateToV3(options.state, { + retreatEvidencePhase: true, + incrementRetreatRevision: true + }); + if (options.state.phase === "verify" || options.state.phase === "archive") { + runRetreat = await stableEvidenceRetreat({ + paths: options.paths, + state: options.state, + migrationId: options.id + }); + } + } + } + const changeFile = path38.join( + nativeChangeDir(options.paths, options.name), + NATIVE_CHANGE_STATE_FILE + ); + const targetContent = (0, import_yaml4.stringify)(migrationStateDocument(nextState)); + return { + schema: "comet.native.schema-migration.v1", + id: options.id, + change: options.name, + fromSchema: options.state.schema, + toSchema: options.state.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? NATIVE_V2_CHANGE_SCHEMA : NATIVE_CHANGE_SCHEMA, + sourceHash: await sha256File(changeFile), + targetHash: sha256Text(targetContent), + createdAt: options.now.toISOString(), + nextState, + ...transition ? { transition } : {}, + ...transitionSupersede ? { transitionSupersede } : {}, + ...runRetreat ? { runRetreat } : {} + }; +} +async function migrateNativeChange(options) { + return withNativeMutationLock( + options.paths, + `migrate schema for ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `migrate schema for ${options.name}`, + async () => { + for (let step = 0; step < 3; step += 1) { + const continued = await continueNativeSchemaMigrationLocked( + options.paths, + options.name, + options.hooks + ); + if (continued?.schema === NATIVE_CHANGE_SCHEMA) return continued; + const inspection = await inspectNativeChange(options.paths, options.name); + if (inspection.status === "current" && inspection.state) { + return inspection.state; + } + if (inspection.status !== "migration-required" || !inspection.state) { + throw new Error( + inspection.message ?? `Native change ${options.name} cannot be migrated` + ); + } + if (await hasPendingNativeCheckpointRecovery(options.paths, options.name)) { + throw new Error( + `Native change ${options.name} has a pending progress checkpoint; recover it with its v2 runtime before schema migration` + ); + } + const state = inspection.state; + if (state.schema !== NATIVE_LEGACY_CHANGE_SCHEMA && state.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Native change ${options.name} has no supported migration route`); + } + const pendingTransition = await inspectPendingNativeTransitionSchema( + options.paths, + options.name + ); + if (pendingTransition?.status === "current") { + throw new Error( + `Native change ${options.name} has a current-schema pending transition; recover it before schema migration` + ); + } + const journal = await prepareNextMigration({ + paths: options.paths, + name: options.name, + state, + pendingTransition, + now: options.now ?? /* @__PURE__ */ new Date(), + id: options.id?.() ?? randomUUID7() + }); + const journalFile = nativeSchemaMigrationJournalFile(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, journalFile); + await atomicWriteJson(journalFile, journal); + await options.hooks?.afterPrepared?.(journal); + } + throw new Error( + `Native change ${options.name} exceeded the supported schema migration path` + ); + } + ) + ); +} + +// domains/comet-native/native-evidence-retention.ts +var NATIVE_EVIDENCE_RETENTION_POLICY = Object.freeze({ + minimumAgeMs: 30 * 24 * 60 * 60 * 1e3, + keepLatestUnreferencedPerKind: 32, + maxDocuments: 4096, + maxScannedBytes: 256 * 1024 * 1024, + maxReportedRefs: 8, + maxReportMessageBytes: 4096 +}); +var MAX_NATIVE_CHECK_RECEIPT_BYTES2 = 512 * 1024; +var HASH_FILE_PATTERN = /^([a-f0-9]{64})\.json$/u; +var CLEANUP_QUARANTINE_PATTERN = /^\.([a-f0-9]{64}\.json)\.([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})\.gc$/u; +var MANAGED_REF_PATTERN = /^runtime\/evidence\/(snapshots|scopes|allowances|verifications|reports|check-receipts)\/[a-f0-9]{64}\.json$/u; +var MANAGED_KINDS = [ + "snapshots", + "scopes", + "allowances", + "verifications", + "reports", + "check-receipts" +]; +function sameObjectIdentity(left, right) { + return sameFileObject( + { ...left, birthtime: left.birthtimeMs }, + { + ...right, + birthtime: right.birthtimeMs + } + ); +} +function sameIdentity(left, right) { + return sameObjectIdentity(left, right) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; +} +function sameContentVersionAfterLinkOrRename(left, right) { + return sameObjectIdentity(left, right) && left.birthtimeMs === right.birthtimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; +} +function fileIdentity2(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function compareText8(left, right) { + return left < right ? -1 : left > right ? 1 : 0; +} +function portableProjectRef(paths, target) { + const relative = path39.relative(paths.projectRoot, target); + if (relative.length === 0 || path39.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path39.sep}`)) { + throw new Error("Native evidence retention finding path is outside the project"); + } + return relative.split(path39.sep).join("/"); +} +function evidenceRootPath(paths, name) { + return path39.join(nativeChangeDir(paths, name), "runtime", "evidence"); +} +function evidenceRootRef(paths, name) { + return portableProjectRef(paths, evidenceRootPath(paths, name)); +} +async function captureDirectoryChain6(managedRoot, directory) { + const root = path39.resolve(managedRoot); + const target = path39.resolve(directory); + if (!isInsidePath(root, target)) { + throw new Error("Native evidence retention path escapes its managed change"); + } + const segments = path39.relative(root, target).split(path39.sep).filter(Boolean); + const chain = []; + let cursor = root; + for (const segment of ["", ...segments]) { + if (segment) cursor = path39.join(cursor, segment); + const stat = await fs26.lstat(cursor); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native evidence retention parent must be a real directory: ${cursor}`); + } + const realPath = await fs26.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new Error(`Native evidence retention parent resolves outside its change: ${cursor}`); + } + chain.push({ path: cursor, realPath, ...fileIdentity2(stat) }); + } + return chain; +} +async function verifyDirectoryChain7(chain) { + for (const identity of chain) { + const stat = await fs26.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameObjectIdentity(identity, stat) || await fs26.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native evidence retention parent changed: ${identity.path}`); + } + } +} +function refForKind(kind, hash6) { + return kind === "check-receipts" ? `runtime/evidence/check-receipts/${hash6}.json` : nativeEvidenceRef(kind, hash6); +} +function managedDependency(value) { + return value !== null && MANAGED_REF_PATTERN.test(value) ? [value] : []; +} +function parseDocument4(kind, hash6, value, expectedChange) { + if (kind === "snapshots") { + return { + canonical: parseNativeSnapshotProjection(value, hash6), + dependencies: [] + }; + } + if (kind === "scopes") { + const scope = parseNativeImplementationScope(value); + if (scope.scopeHash !== hash6) throw new Error("Native scope filename does not match its hash"); + return { + canonical: scope, + dependencies: [scope.baselineProjectionRef, scope.currentProjectionRef] + }; + } + if (kind === "allowances") { + const allowance = parseNativePartialAllowance(value); + if (allowance.change !== expectedChange || allowance.allowanceHash !== hash6) { + throw new Error("Native allowance filename/change does not match its content"); + } + return { + canonical: allowance, + dependencies: [nativeEvidenceRef("scopes", allowance.scopeHash)] + }; + } + if (kind === "verifications") { + const evidence = parseNativeVerificationEvidenceEnvelope(value); + if (evidence.change !== expectedChange || evidence.envelopeHash !== hash6) { + throw new Error("Native verification filename/change does not match its content"); + } + const traceDependencies = evidence.acceptanceTrace.entries.flatMap( + (entry2) => entry2.evidenceRefs.flatMap(managedDependency) + ); + return { + canonical: evidence, + dependencies: [ + evidence.implementationScopeRef, + nativeReportEvidenceRef(evidence.reportHash), + ...managedDependency(evidence.partialAllowanceRef), + ...managedDependency(evidence.receiptRef), + ...traceDependencies + ] + }; + } + if (kind === "reports") { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native report evidence must be an object"); + } + const report = value; + if (Object.keys(report).sort().join(",") !== "content,reportHash,schema" || report.schema !== "comet.native.verification-report.v1" || report.reportHash !== hash6 || typeof report.content !== "string" || createHash13("sha256").update(Buffer.from(report.content, "utf8")).digest("hex") !== hash6) { + throw new Error("Native report evidence filename/hash does not match its content"); + } + return { canonical: report, dependencies: [] }; + } + const receipt = parseNativeCheckReceipt(value); + if (receipt.change !== expectedChange || receipt.receiptHash !== hash6) { + throw new Error("Native check receipt filename/change does not match its content"); + } + return { canonical: receipt, dependencies: [] }; +} +async function readCanonicalDocument(options) { + const before = await fs26.lstat(options.file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native evidence entry is not a regular file: ${options.ref}`); + } + const maximumBytes = options.kind === "check-receipts" ? MAX_NATIVE_CHECK_RECEIPT_BYTES2 : MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES; + if (before.size > maximumBytes) { + throw new Error(`Native evidence entry exceeds its byte budget: ${options.ref}`); + } + const beforeRealPath = await fs26.realpath(options.file); + if (!isInsidePath(options.directoryChain[0].realPath, beforeRealPath)) { + throw new Error(`Native evidence entry resolves outside its change: ${options.ref}`); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants5.O_RDONLY | fsConstants5.O_NOFOLLOW | fsConstants5.O_NONBLOCK; + const handle = await fs26.open(options.file, openFlags).catch((error) => { + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new Error(`Native evidence entry became unsafe while opening: ${options.ref}`); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs26.lstat(options.file), + fs26.realpath(options.file) + ]); + await verifyDirectoryChain7(options.directoryChain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameIdentity(fileIdentity2(before), opened) || !sameIdentity(fileIdentity2(opened), pathAfterOpen)) { + throw new Error(`Native evidence entry changed while opening: ${options.ref}`); + } + const chunks = []; + const buffer = Buffer.allocUnsafe(64 * 1024); + let total = 0; + while (true) { + const remaining = maximumBytes + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > maximumBytes) { + throw new Error(`Native evidence entry exceeds its byte budget: ${options.ref}`); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs26.lstat(options.file), + fs26.realpath(options.file) + ]); + await verifyDirectoryChain7(options.directoryChain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameIdentity(fileIdentity2(opened), afterHandle) || !sameIdentity(fileIdentity2(opened), afterPath)) { + throw new Error(`Native evidence entry changed while reading: ${options.ref}`); + } + const text = Buffer.concat(chunks, total).toString("utf8"); + let json; + try { + json = JSON.parse(text); + } catch (error) { + throw new Error(`Native evidence entry is not valid JSON: ${options.ref}`, { + cause: error + }); + } + const parsed = parseDocument4(options.kind, options.hash, json, options.name); + if (text !== JSON.stringify(parsed.canonical, null, 2) + "\n") { + throw new Error(`Native evidence entry is not canonically serialized: ${options.ref}`); + } + return { + ref: options.ref, + kind: options.kind, + change: options.name, + changeRoot: options.changeRoot, + file: options.file, + size: total, + mtimeMs: afterHandle.mtimeMs, + identity: fileIdentity2(afterHandle), + directoryChain: options.directoryChain, + dependencies: [...new Set(parsed.dependencies)].sort(compareText8) + }; + } finally { + await handle.close(); + } +} +async function pathIsMissing(file) { + try { + await fs26.lstat(file); + return false; + } catch (error) { + if (error.code === "ENOENT") return true; + throw error; + } +} +async function inspectCleanupQuarantines(options) { + const changeRoot = nativeChangeDir(options.paths, options.name); + const evidenceRoot = evidenceRootPath(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, evidenceRoot); + let rootEntries; + try { + const rootChain = await captureDirectoryChain6(changeRoot, evidenceRoot); + rootEntries = await fs26.readdir(evidenceRoot, { withFileTypes: true }); + await verifyDirectoryChain7(rootChain); + } catch (error) { + if (error.code === "ENOENT") { + return { findings: [], pending: false }; + } + throw error; + } + const recoveries = []; + for (const kind of MANAGED_KINDS) { + if (!rootEntries.some((entry2) => entry2.name === kind)) continue; + const directory = path39.join(evidenceRoot, kind); + const directoryChain = await captureDirectoryChain6(changeRoot, directory); + const entries = (await fs26.readdir(directory, { withFileTypes: true })).sort( + (left, right) => compareText8(left.name, right.name) + ); + const byOriginal = /* @__PURE__ */ new Map(); + for (const entry2 of entries) { + const match = CLEANUP_QUARANTINE_PATTERN.exec(entry2.name); + if (!match) continue; + const grouped = byOriginal.get(match[1]) ?? []; + grouped.push(entry2); + byOriginal.set(match[1], grouped); + } + for (const [originalName, entriesForOriginal] of [...byOriginal.entries()].sort( + ([left], [right]) => compareText8(left, right) + )) { + if (entriesForOriginal.length !== 1) { + throw new Error( + `Native evidence cleanup has multiple quarantines for ${kind}/${originalName}` + ); + } + const entry2 = entriesForOriginal[0]; + if (!entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error( + `Native evidence cleanup quarantine is not a regular file: ${kind}/${entry2.name}` + ); + } + const original = path39.join(directory, originalName); + const hash6 = HASH_FILE_PATTERN.exec(originalName)?.[1]; + if (!hash6) + throw new Error(`Native evidence cleanup quarantine name is invalid: ${entry2.name}`); + const ref = refForKind(kind, hash6); + const quarantine = path39.join(directory, entry2.name); + const document = await readCanonicalDocument({ + changeRoot, + file: quarantine, + ref, + kind, + hash: hash6, + name: options.name, + directoryChain + }); + const originalMissing = await pathIsMissing(original); + const originalDocument = originalMissing ? null : await readCanonicalDocument({ + changeRoot, + file: original, + ref, + kind, + hash: hash6, + name: options.name, + directoryChain + }); + recoveries.push({ + ref, + original, + quarantine, + document, + action: originalMissing ? "restore-original" : "finish-linked-recovery", + originalDocument + }); + } + } + if (recoveries.length === 0) return { findings: [], pending: false }; + const refs = recoveries.map((recovery) => recovery.ref).sort(compareText8); + if (!options.repair) { + return { + pending: true, + findings: [ + { + severity: "warning", + code: "evidence-retention-recovery-required", + message: boundedMessage( + `Native evidence cleanup has ${refs.length} safely recoverable quarantined document(s) for ${options.name}: ${boundedRefs(refs)}` + ), + path: evidenceRootRef(options.paths, options.name) + } + ] + }; + } + for (const recovery of recoveries.sort((left, right) => compareText8(left.ref, right.ref))) { + await verifyDirectoryChain7(recovery.document.directoryChain); + if (recovery.action === "finish-linked-recovery") { + const original = await readCanonicalDocument({ + changeRoot, + file: recovery.original, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + const quarantine = await readCanonicalDocument({ + changeRoot, + file: recovery.quarantine, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + if (!recovery.originalDocument || !sameIdentity(recovery.originalDocument.identity, original.identity) || !sameIdentity(recovery.document.identity, quarantine.identity)) { + throw new Error( + `Native evidence cleanup original or quarantine changed during recovery: ${recovery.ref}` + ); + } + await fs26.rm(recovery.quarantine); + await verifyDirectoryChain7(recovery.document.directoryChain); + continue; + } + if (!await pathIsMissing(recovery.original)) { + throw new Error(`Native evidence cleanup original appeared during recovery: ${recovery.ref}`); + } + const quarantineStat = await fs26.lstat(recovery.quarantine); + if (!quarantineStat.isFile() || quarantineStat.isSymbolicLink() || !sameIdentity(recovery.document.identity, quarantineStat)) { + throw new Error( + `Native evidence cleanup quarantine changed during recovery: ${recovery.ref}` + ); + } + await fs26.link(recovery.quarantine, recovery.original); + await options.hooks.afterRecoveryLink?.({ + ref: recovery.ref, + original: recovery.original, + quarantine: recovery.quarantine + }); + const restored = await readCanonicalDocument({ + changeRoot, + file: recovery.original, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + const linkedQuarantine = await readCanonicalDocument({ + changeRoot, + file: recovery.quarantine, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + if (!sameContentVersionAfterLinkOrRename(recovery.document.identity, restored.identity) || !sameIdentity(restored.identity, linkedQuarantine.identity)) { + throw new Error(`Native evidence cleanup recovery identity changed: ${recovery.ref}`); + } + await fs26.rm(recovery.quarantine); + await verifyDirectoryChain7(recovery.document.directoryChain); + } + return { + pending: false, + findings: [ + { + severity: "info", + code: "evidence-retention-cleanup-recovered", + message: boundedMessage( + `Recovered ${refs.length} interrupted Native evidence cleanup document(s) for ${options.name}: ${boundedRefs(refs)}` + ), + path: evidenceRootRef(options.paths, options.name) + } + ] + }; +} +async function scanEvidenceStore(paths, name) { + const changeRoot = nativeChangeDir(paths, name); + const evidenceRoot = path39.join(changeRoot, "runtime", "evidence"); + await resolveContainedNativePath(paths.nativeRoot, evidenceRoot); + let rootEntries; + try { + const rootChain = await captureDirectoryChain6(changeRoot, evidenceRoot); + rootEntries = await fs26.readdir(evidenceRoot, { withFileTypes: true }); + await verifyDirectoryChain7(rootChain); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const unknownRootEntries = rootEntries.filter( + (entry2) => !MANAGED_KINDS.includes(entry2.name) || !entry2.isDirectory() || entry2.isSymbolicLink() + ).map((entry2) => entry2.name).sort(compareText8); + if (unknownRootEntries.length > 0) { + throw new Error( + `Native evidence root has unknown or special entries: ${boundedRefs(unknownRootEntries)}` + ); + } + const documents = []; + let scannedBytes = 0; + for (const kind of MANAGED_KINDS) { + if (!rootEntries.some((entry2) => entry2.name === kind)) continue; + const directory = path39.join(evidenceRoot, kind); + const directoryChain = await captureDirectoryChain6(changeRoot, directory); + const entries = (await fs26.readdir(directory, { withFileTypes: true })).sort( + (left, right) => compareText8(left.name, right.name) + ); + await verifyDirectoryChain7(directoryChain); + for (const entry2 of entries) { + if (documents.length >= NATIVE_EVIDENCE_RETENTION_POLICY.maxDocuments) { + throw new Error( + `Native evidence store exceeds ${NATIVE_EVIDENCE_RETENTION_POLICY.maxDocuments} documents` + ); + } + const match = HASH_FILE_PATTERN.exec(entry2.name); + if (!match || !entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error( + `Native evidence kind ${kind} has an unknown or special entry: ${entry2.name}` + ); + } + const ref = refForKind(kind, match[1]); + const file = path39.join(directory, entry2.name); + await resolveContainedNativePath(paths.nativeRoot, file); + const document = await readCanonicalDocument({ + changeRoot, + file, + ref, + kind, + hash: match[1], + name, + directoryChain + }); + scannedBytes += document.size; + if (scannedBytes > NATIVE_EVIDENCE_RETENTION_POLICY.maxScannedBytes) { + throw new Error( + `Native evidence store exceeds ${NATIVE_EVIDENCE_RETENTION_POLICY.maxScannedBytes} scanned bytes` + ); + } + documents.push(document); + } + await verifyDirectoryChain7(directoryChain); + } + return documents; +} +function closeDependencies(seeds, documents) { + const retained = /* @__PURE__ */ new Set(); + const pending = [...seeds].sort(compareText8).reverse(); + while (pending.length > 0) { + const ref = pending.pop(); + if (retained.has(ref)) continue; + const document = documents.get(ref); + if (!document) throw new Error(`Native evidence dependency is missing: ${ref}`); + retained.add(ref); + for (const dependency of [...document.dependencies].sort(compareText8).reverse()) { + if (!retained.has(dependency)) pending.push(dependency); + } + } + return retained; +} +function dependentsBeforeDependencies(candidates) { + const byRef = new Map(candidates.map((candidate) => [candidate.ref, candidate])); + const dependencyCounts = new Map(candidates.map((candidate) => [candidate.ref, 0])); + for (const candidate of candidates) { + for (const dependency of candidate.dependencies) { + if (byRef.has(dependency)) { + dependencyCounts.set(dependency, (dependencyCounts.get(dependency) ?? 0) + 1); + } + } + } + const ready = [...dependencyCounts].filter(([, count]) => count === 0).map(([ref]) => ref).sort(compareText8); + const ordered = []; + while (ready.length > 0) { + const ref = ready.shift(); + const candidate = byRef.get(ref); + ordered.push(candidate); + for (const dependency of candidate.dependencies) { + const count = dependencyCounts.get(dependency); + if (count === void 0) continue; + const next = count - 1; + dependencyCounts.set(dependency, next); + if (next === 0) { + ready.push(dependency); + ready.sort(compareText8); + } + } + } + if (ordered.length !== candidates.length) { + throw new Error("Native evidence candidate dependency graph contains a cycle"); + } + return ordered; +} +function planRetention(documents, rootRefs, nowMs) { + const byRef = new Map(documents.map((document) => [document.ref, document])); + if (byRef.size !== documents.length) throw new Error("Native evidence store has duplicate refs"); + for (const document of documents) { + for (const dependency of document.dependencies) { + if (!byRef.has(dependency)) { + throw new Error(`Native evidence dependency is missing: ${dependency}`); + } + } + } + const rootClosure = closeDependencies(rootRefs, byRef); + const retainedSeeds = new Set(rootClosure); + for (const document of documents) { + if (nowMs - document.mtimeMs < NATIVE_EVIDENCE_RETENTION_POLICY.minimumAgeMs) { + retainedSeeds.add(document.ref); + } + } + for (const kind of MANAGED_KINDS) { + const newestUnreferenced = documents.filter((document) => document.kind === kind && !rootClosure.has(document.ref)).sort((left, right) => right.mtimeMs - left.mtimeMs || compareText8(left.ref, right.ref)).slice(0, NATIVE_EVIDENCE_RETENTION_POLICY.keepLatestUnreferencedPerKind); + for (const document of newestUnreferenced) retainedSeeds.add(document.ref); + } + const retained = closeDependencies(retainedSeeds, byRef); + const candidates = documents.filter( + (document) => !retained.has(document.ref) && nowMs - document.mtimeMs >= NATIVE_EVIDENCE_RETENTION_POLICY.minimumAgeMs + ).sort((left, right) => compareText8(left.ref, right.ref)); + return { + candidates: dependentsBeforeDependencies(candidates), + candidateBytes: candidates.reduce((total, candidate) => total + candidate.size, 0) + }; +} +function boundedRefs(refs) { + const shown = refs.slice(0, NATIVE_EVIDENCE_RETENTION_POLICY.maxReportedRefs); + const omitted = refs.length - shown.length; + return `${shown.join(", ")}${omitted > 0 ? `, ... (${omitted} more)` : ""}`; +} +function boundedMessage(message) { + const maximum = NATIVE_EVIDENCE_RETENTION_POLICY.maxReportMessageBytes; + if (Buffer.byteLength(message, "utf8") <= maximum) return message; + let result2 = message; + while (result2.length > 0 && Buffer.byteLength(`${result2}...`, "utf8") > maximum) { + result2 = result2.slice(0, -1); + } + return `${result2}...`; +} +function summaryFinding(name, plan, repaired, evidenceRoot) { + const refs = plan.candidates.map((candidate) => candidate.ref).sort(compareText8); + return { + severity: "info", + code: repaired ? "evidence-retention-cleaned" : "evidence-retention-candidates", + message: boundedMessage( + `${repaired ? "Removed" : "Found"} ${plan.candidates.length} old unreferenced Native evidence document(s) (${plan.candidateBytes} bytes) for ${name}: ${boundedRefs(refs)}` + ), + path: evidenceRoot + }; +} +async function restoreQuarantineIfSafe(quarantine, original) { + try { + await fs26.lstat(original); + return; + } catch (error) { + if (error.code !== "ENOENT") return; + } + try { + await fs26.rename(quarantine, original); + } catch { + } +} +async function deleteCandidate(candidate, hooks) { + await hooks.beforeDelete?.({ ref: candidate.ref, file: candidate.file }); + await verifyDirectoryChain7(candidate.directoryChain); + const beforeDocument = await readCanonicalDocument({ + changeRoot: candidate.changeRoot, + file: candidate.file, + ref: candidate.ref, + kind: candidate.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(candidate.file))[1], + name: candidate.change, + directoryChain: candidate.directoryChain + }); + const before = await fs26.lstat(candidate.file); + const beforeRealPath = await fs26.realpath(candidate.file); + if (!before.isFile() || before.isSymbolicLink() || !sameIdentity(candidate.identity, before) || !sameIdentity(candidate.identity, beforeDocument.identity) || !isInsidePath(candidate.directoryChain[0].realPath, beforeRealPath)) { + throw new Error(`Native evidence candidate changed before cleanup: ${candidate.ref}`); + } + const quarantine = path39.join( + path39.dirname(candidate.file), + `.${path39.basename(candidate.file)}.${randomUUID8()}.gc` + ); + await fs26.rename(candidate.file, quarantine); + try { + const [moved, movedRealPath] = await Promise.all([ + fs26.lstat(quarantine), + fs26.realpath(quarantine) + ]); + await verifyDirectoryChain7(candidate.directoryChain); + if (!moved.isFile() || moved.isSymbolicLink() || !sameContentVersionAfterLinkOrRename(candidate.identity, moved) || !isInsidePath(candidate.directoryChain[0].realPath, movedRealPath)) { + await restoreQuarantineIfSafe(quarantine, candidate.file); + throw new Error(`Native evidence candidate changed during cleanup: ${candidate.ref}`); + } + await fs26.rm(quarantine); + await verifyDirectoryChain7(candidate.directoryChain); + } catch (error) { + await restoreQuarantineIfSafe(quarantine, candidate.file); + throw error; + } +} +async function hasPendingRelocationOrArchive(paths) { + const config = await readProjectConfig(paths.projectRoot); + if (config?.native.pending_root_move) return true; + let entries; + try { + entries = await fs26.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) return true; + try { + const transaction = await readNativeTransaction(paths, entry2.name); + if (transaction.status !== "committed" && transaction.status !== "rolled-back") return true; + } catch { + return true; + } + } + return false; +} +async function changeNames(paths, requested) { + if (requested) return [requested]; + let entries; + try { + entries = await fs26.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + return entries.filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(compareText8); +} +async function retentionDeferred(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status !== "current" || !inspection.state || inspection.state.archived) + return true; + const [transition, migration, checkpoint] = await Promise.all([ + inspectPendingNativeTransition(paths, name), + inspectPendingNativeSchemaMigration(paths, name), + readNativeCheckpointJournal(paths, name) + ]); + return transition !== null || migration !== null || checkpoint !== null; +} +async function collectChangeRetention(options) { + if (await retentionDeferred(options.paths, options.name)) return []; + const inspection = await inspectNativeChange(options.paths, options.name); + if (inspection.status !== "current" || !inspection.state) return []; + if (!("implementation_scope" in inspection.state)) return []; + const cleanupRecovery = await inspectCleanupQuarantines({ + paths: options.paths, + name: options.name, + repair: options.repair, + hooks: options.hooks + }); + if (cleanupRecovery.pending) return cleanupRecovery.findings; + const rootRefs = []; + for (const value of [ + inspection.state.implementation_scope, + inspection.state.partial_allowance, + inspection.state.verification_evidence + ]) { + if (value !== null) rootRefs.push(value); + } + const documents = await scanEvidenceStore(options.paths, options.name); + const plan = planRetention(documents, rootRefs, options.nowMs); + if (plan.candidates.length === 0) return cleanupRecovery.findings; + const evidenceRef = evidenceRootRef(options.paths, options.name); + if (!options.repair) { + return [...cleanupRecovery.findings, summaryFinding(options.name, plan, false, evidenceRef)]; + } + const deleted = []; + for (const candidate of plan.candidates) { + try { + await deleteCandidate(candidate, options.hooks); + deleted.push(candidate); + } catch (error) { + const deletedBytes = deleted.reduce((total, item) => total + item.size, 0); + return [ + ...cleanupRecovery.findings, + ...deleted.length > 0 ? [ + summaryFinding( + options.name, + { candidates: deleted, candidateBytes: deletedBytes }, + true, + evidenceRef + ) + ] : [], + { + severity: "error", + code: "evidence-retention-cleanup-failed", + message: boundedMessage( + `Native evidence cleanup stopped safely for ${options.name}: ${error.message}` + ), + path: evidenceRef + } + ]; + } + } + return [...cleanupRecovery.findings, summaryFinding(options.name, plan, true, evidenceRef)]; +} +async function inspectNativeEvidenceRetention(options) { + if (options.deferAll || await hasPendingRelocationOrArchive(options.paths)) return []; + const now = options.now ?? /* @__PURE__ */ new Date(); + if (Number.isNaN(now.getTime())) throw new Error("Native evidence retention time is invalid"); + const run = async (repair) => { + const findings = []; + for (const name of await changeNames(options.paths, options.name)) { + try { + findings.push( + ...await collectChangeRetention({ + paths: options.paths, + name, + repair, + nowMs: now.getTime(), + hooks: options.hooks ?? {} + }) + ); + } catch (error) { + findings.push({ + severity: "error", + code: "evidence-retention-unsafe", + message: boundedMessage( + `Native evidence retention refused to clean ${name}: ${error.message}` + ), + path: evidenceRootRef(options.paths, name) + }); + } + } + return findings; + }; + if (!(options.repair ?? false)) return run(false); + const preflight = await run(false); + if (preflight.some((finding) => finding.severity === "error")) return preflight; + if (!preflight.some( + (finding) => finding.code === "evidence-retention-candidates" || finding.code === "evidence-retention-recovery-required" + )) { + return preflight; + } + try { + return await withNativeMutationLock(options.paths, "prune Native evidence", () => run(true)); + } catch (error) { + return [ + { + severity: "info", + code: "evidence-retention-cleanup-deferred", + message: boundedMessage( + `Native evidence cleanup was deferred until the project mutation lock is available: ${error.message}` + ) + } + ]; + } +} + +// domains/comet-native/native-root-move.ts +import { createHash as createHash14, randomUUID as randomUUID9 } from "crypto"; +import { promises as fs27 } from "fs"; +import path40 from "path"; +var NATIVE_ROOT_MOVE_MAX_FILE_BYTES = 64 * 1024 * 1024; +var NATIVE_ROOT_MOVE_MAX_JOURNAL_BYTES = 256 * 1024; +var NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES = 16 * 1024 * 1024; +async function exists3(target) { + try { + await fs27.access(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function assertNoUnfinishedTransactions(paths) { + let entries; + try { + entries = await fs27.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let journal; + try { + journal = await readNativeTransaction(paths, entry2.name); + } catch (error) { + if (error.code === "ENOENT") { + throw new Error( + `Native transaction ${entry2.name} has no journal; run doctor before moving`, + { cause: error } + ); + } + throw error; + } + if (journal.status !== "committed" && journal.status !== "rolled-back") { + throw new Error(`Native transaction ${journal.id} is unfinished; recover it before moving`); + } + } +} +async function assertNoOtherLocks(paths, ownedLock) { + for (const entry2 of await fs27.readdir(paths.locksDir, { withFileTypes: true })) { + const file = path40.join(paths.locksDir, entry2.name); + if (path40.resolve(file) === path40.resolve(ownedLock)) continue; + if (entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error(`Native lock must be diagnosed before moving the root: ${file}`); + } + } +} +async function refreshNativeWorkspaceIdentities(paths) { + let entries; + try { + entries = await fs27.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + for (const entry2 of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let inspection; + try { + inspection = await inspectNativeChange(paths, entry2.name); + } catch { + continue; + } + if (inspection.status !== "current" || !inspection.state || !("revision" in inspection.state)) { + continue; + } + await writeNativeWorkspaceIdentity({ + paths, + name: entry2.name, + revision: inspection.state.revision + }); + } +} +async function walkTree(root, options) { + const treeEntries = []; + async function visit(directory) { + const protectedDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: "Native root tree" + }); + const entries = protectedDirectory.entries; + entries.sort((left, right) => left.name.localeCompare(right.name)); + for (const entry2 of entries) { + await protectedDirectory.verify(); + const target = path40.join(directory, entry2.name); + if (options.excludedFiles?.has(path40.resolve(target))) { + await protectedDirectory.verify(); + continue; + } + const stat = await fs27.lstat(target); + if (entry2.isSymbolicLink() || stat.isSymbolicLink()) { + if (options.rejectSymlinks) throw new Error(`Native root contains a symlink: ${target}`); + await protectedDirectory.verify(); + continue; + } + if (entry2.isDirectory() && stat.isDirectory()) { + treeEntries.push({ + ref: path40.relative(root, target).split(path40.sep).join("/"), + type: "directory" + }); + await visit(target); + } else if (entry2.isFile() && stat.isFile()) { + const snapshot2 = await readNativeProtectedFile({ + root, + file: target, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + label: `Native root file ${path40.relative(root, target)}` + }); + treeEntries.push({ + ref: path40.relative(root, target).split(path40.sep).join("/"), + type: "file", + size: snapshot2.size, + hash: snapshot2.hash + }); + } else { + throw new Error(`Native root contains an unsupported file type: ${target}`); + } + await protectedDirectory.verify(); + } + } + await visit(root); + return treeEntries.sort((left, right) => left.ref.localeCompare(right.ref)); +} +async function copyTree(source, target, excludedFile, targetRoot) { + await ensureNativeProtectedDirectory({ + root: targetRoot, + directory: target, + label: "Native root move staging directory" + }); + async function copyDirectory(from, to) { + const protectedDirectory = await readNativeProtectedDirectory({ + root: source, + directory: from, + label: "Native root move source directory" + }); + const entries = protectedDirectory.entries; + for (const entry2 of entries) { + await protectedDirectory.verify(); + const sourceEntry = path40.join(from, entry2.name); + if (path40.resolve(sourceEntry) === path40.resolve(excludedFile)) { + await protectedDirectory.verify(); + continue; + } + const stat = await fs27.lstat(sourceEntry); + if (entry2.isSymbolicLink() || stat.isSymbolicLink()) { + throw new Error(`Native root contains a symlink: ${sourceEntry}`); + } + const targetEntry = path40.join(to, entry2.name); + if (entry2.isDirectory() && stat.isDirectory()) { + await ensureNativeProtectedDirectory({ + root: targetRoot, + directory: targetEntry, + label: "Native root move staging directory" + }); + await copyDirectory(sourceEntry, targetEntry); + } else if (entry2.isFile() && stat.isFile()) { + await copyNativeProtectedFile({ + sourceRoot: source, + source: sourceEntry, + targetRoot, + target: targetEntry, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + label: `Native root move file ${path40.relative(source, sourceEntry)}`, + exclusive: true, + expectedTargetHash: null + }); + } else { + throw new Error(`Native root contains an unsupported file type: ${sourceEntry}`); + } + await protectedDirectory.verify(); + } + } + await copyDirectory(source, target); +} +async function assertEquivalentTrees(source, target, excludedSourceLock, excludedTargetLock) { + const sourceFiles = await walkTree(source, { + rejectSymlinks: true, + excludedFiles: excludedSourceLock ? /* @__PURE__ */ new Set([path40.resolve(excludedSourceLock)]) : void 0 + }); + const targetFiles = await walkTree(target, { + rejectSymlinks: true, + excludedFiles: excludedTargetLock ? /* @__PURE__ */ new Set([path40.resolve(excludedTargetLock)]) : void 0 + }); + if (JSON.stringify(sourceFiles) !== JSON.stringify(targetFiles)) { + throw new Error( + `Native root copies differ; preserve both trees for manual recovery: ${source} and ${target}` + ); + } +} +function stagingDirectory(targetPaths, id) { + return path40.join(targetPaths.artifactRoot, `.comet-native-move-${id}`); +} +function sourceRemovalDirectory(sourcePaths, id) { + return path40.join(sourcePaths.artifactRoot, `.comet-native-source-${id}.removing`); +} +function stagingRemovalDirectory(staging) { + return `${staging}.removing`; +} +function rollbackRemovalDirectory(target, id) { + return path40.join(path40.dirname(target), `.${path40.basename(target)}.${id}.rollback-removing`); +} +function cleanupManifestFile(paths, id, kind) { + return path40.join(nativeTransactionPaths(paths, id).directory, `root-move-cleanup-${kind}.json`); +} +function cleanupManifestSource(manifest) { + return JSON.stringify(manifest, null, 2) + "\n"; +} +function cleanupManifestHash(source) { + return createHash14("sha256").update(source).digest("hex"); +} +function parseCleanupManifestEntry(value, index) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Native root-move cleanup manifest entry ${index} must be an object`); + } + const entry2 = value; + const keys = Object.keys(entry2).sort(); + const expectedKeys = entry2.type === "file" ? ["hash", "ref", "size", "type"] : ["ref", "type"]; + if (JSON.stringify(keys) !== JSON.stringify(expectedKeys)) { + throw new Error(`Native root-move cleanup manifest entry ${index} has invalid fields`); + } + if (typeof entry2.ref !== "string" || entry2.ref.length === 0 || entry2.ref.includes("\\") || path40.posix.normalize(entry2.ref) !== entry2.ref || entry2.ref.startsWith("/") || entry2.ref.split("/").includes("..") || Buffer.byteLength(entry2.ref, "utf8") > 4096) { + throw new Error(`Native root-move cleanup manifest entry ${index} has an invalid ref`); + } + if (entry2.type === "directory") return { ref: entry2.ref, type: "directory" }; + if (entry2.type !== "file" || typeof entry2.size !== "number" || !Number.isSafeInteger(entry2.size) || entry2.size < 0 || typeof entry2.hash !== "string" || !/^[a-f0-9]{64}$/u.test(entry2.hash)) { + throw new Error(`Native root-move cleanup manifest entry ${index} is invalid`); + } + return { ref: entry2.ref, type: "file", size: entry2.size, hash: entry2.hash }; +} +function parseCleanupManifest(value, id, kind) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native root-move cleanup manifest must be an object"); + } + const manifest = value; + if (JSON.stringify(Object.keys(manifest).sort()) !== JSON.stringify(["entries", "kind", "schema", "transactionId"]) || manifest.schema !== "comet.native.root-move-cleanup.v1" || manifest.transactionId !== id || manifest.kind !== kind || !Array.isArray(manifest.entries)) { + throw new Error("Native root-move cleanup manifest binding is invalid"); + } + const entries = manifest.entries.map(parseCleanupManifestEntry); + for (let index = 0; index < entries.length; index += 1) { + if (index > 0 && entries[index - 1].ref.localeCompare(entries[index].ref) >= 0) { + throw new Error("Native root-move cleanup manifest refs must be unique and sorted"); + } + const parent = path40.posix.dirname(entries[index].ref); + if (parent !== "." && !entries.some((candidate) => candidate.ref === parent && candidate.type === "directory")) { + throw new Error(`Native root-move cleanup manifest parent is missing: ${parent}`); + } + } + return { + schema: "comet.native.root-move-cleanup.v1", + transactionId: id, + kind, + entries + }; +} +async function writeCleanupManifest(options) { + const manifest = { + schema: "comet.native.root-move-cleanup.v1", + transactionId: options.id, + kind: options.kind, + entries: options.entries + }; + const source = cleanupManifestSource(manifest); + if (Buffer.byteLength(source, "utf8") > NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES) { + throw new Error("Native root-move cleanup manifest exceeds its byte budget"); + } + await atomicWriteText(cleanupManifestFile(options.paths, options.id, options.kind), source, { + containedRoot: options.paths.nativeRoot + }); + return cleanupManifestHash(source); +} +async function readCleanupManifest(options) { + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: cleanupManifestFile(options.paths, options.id, options.cleanup.kind), + maxBytes: NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES, + label: `Native root-move cleanup manifest ${options.cleanup.kind}` + }); + if (snapshot2.hash !== options.cleanup.manifestHash) { + throw new Error("Native root-move cleanup manifest hash changed"); + } + return parseCleanupManifest( + JSON.parse(snapshot2.bytes.toString("utf8")), + options.id, + options.cleanup.kind + ); +} +async function assertCleanupManifestMatch(options) { + const current = await walkTree(options.quarantine, { rejectSymlinks: true }); + const expected = new Map(options.manifest.entries.map((entry2) => [entry2.ref, entry2])); + for (const entry2 of current) { + const bound = expected.get(entry2.ref); + if (!bound || JSON.stringify(bound) !== JSON.stringify(entry2)) { + throw new Error( + `Native root-move cleanup quarantine differs from its bound manifest: ${entry2.ref}` + ); + } + } + if (options.exact && current.length !== options.manifest.entries.length) { + throw new Error("Native root-move cleanup quarantine is incomplete before deletion"); + } + return current; +} +function treeEntryDepth(entry2) { + return entry2.ref.split("/").length; +} +async function deleteCleanupManifestSubset(options) { + const current = await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest: options.manifest, + exact: false + }); + const ordered = [...current].sort( + (left, right) => treeEntryDepth(right) - treeEntryDepth(left) || (left.type === right.type ? right.ref.localeCompare(left.ref) : left.type === "file" ? -1 : 1) + ); + let removedCount = 0; + for (const entry2 of ordered) { + const target = path40.join(options.quarantine, ...entry2.ref.split("/")); + if (entry2.type === "file") { + await removeNativeProtectedFile({ + root: options.projectRoot, + file: target, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + expectedHash: entry2.hash, + expectedSize: entry2.size, + label: `Native root-move cleanup file ${entry2.ref}` + }); + } else { + await removeNativeProtectedEmptyDirectory({ + root: options.projectRoot, + directory: target, + label: `Native root-move cleanup directory ${entry2.ref}` + }); + } + removedCount += 1; + await options.hooks?.afterRootMoveCleanupEntryRemoved?.( + options.manifest.kind, + entry2.ref, + removedCount + ); + } + await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest: options.manifest, + exact: false + }); + await removeNativeProtectedEmptyDirectory({ + root: options.projectRoot, + directory: options.quarantine, + label: `Native root-move cleanup quarantine ${options.manifest.kind}` + }); +} +function pendingConfig(config, pending, activeArtifactRoot = config.native.artifact_root) { + return { + ...config, + native: { ...config.native, artifact_root: activeArtifactRoot, pending_root_move: pending } + }; +} +function rootMoveJournal(options) { + return { + schema: "comet.native.transaction.v1", + id: options.id, + kind: "root-move", + status: "prepared", + projectRoot: options.paths.projectRoot, + nativeRoot: options.paths.nativeRoot, + createdAt: options.now.toISOString(), + operations: [] + }; +} +async function readRootMoveJournal(sourcePaths, destinationPaths, stage, id) { + for (const paths of [sourcePaths, destinationPaths]) { + try { + const journal = await readNativeTransaction(paths, id); + if (journal.kind !== "root-move") throw new Error(`Transaction ${id} is not a root move`); + return { journal, paths }; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + } + const stageJournal = path40.join(stage, "runtime", "transactions", id, "transaction.json"); + try { + const snapshot2 = await readNativeProtectedFile({ + root: stage, + file: stageJournal, + maxBytes: NATIVE_ROOT_MOVE_MAX_JOURNAL_BYTES, + label: `Staged Native root-move journal ${id}` + }); + const journal = JSON.parse(snapshot2.bytes.toString("utf8")); + if (journal.schema !== "comet.native.transaction.v1" || journal.kind !== "root-move") { + throw new Error(`Invalid staged root-move journal: ${id}`); + } + return { journal, paths: destinationPaths }; + } catch (error) { + if (error.code !== "ENOENT") throw error; + throw new Error(`Native root-move journal is missing: ${id}`, { cause: error }); + } +} +async function setPendingStage(options) { + const updated = pendingConfig( + options.config, + { ...options.pending, stage: options.stage }, + options.activeArtifactRoot + ); + await writeProjectConfig(options.projectRoot, updated); + return updated; +} +async function setPendingCleanup(options) { + const pending = options.config.native.pending_root_move; + if (!pending || pending.id !== options.id) { + throw new Error(`Native root-move cleanup ${options.id} lost its pending configuration`); + } + const updated = pendingConfig( + options.config, + { + id: pending.id, + fromArtifactRoot: pending.fromArtifactRoot, + toArtifactRoot: pending.toArtifactRoot, + stage: pending.stage, + ...options.cleanup ? { cleanup: options.cleanup } : {} + }, + options.config.native.artifact_root + ); + await writeProjectConfig(options.projectRoot, updated); + return updated; +} +async function cleanupRootMoveTree(options) { + let config = options.config; + const pending = config.native.pending_root_move; + if (!pending || pending.id !== options.id) { + throw new Error(`Native root-move cleanup ${options.id} lost its pending configuration`); + } + let cleanup = pending.cleanup; + if (cleanup && cleanup.kind !== options.kind) { + throw new Error( + `Native root move has unfinished ${cleanup.kind} cleanup; cannot start ${options.kind}` + ); + } + let targetExists = await exists3(options.target); + let quarantineExists = await exists3(options.quarantine); + if (targetExists && quarantineExists) { + throw new Error(`Native root-move cleanup target and quarantine both exist: ${options.kind}`); + } + if (!cleanup) { + if (!targetExists && !quarantineExists) return config; + if (!targetExists && quarantineExists) { + throw new Error( + `Native root-move cleanup quarantine is not transaction-bound: ${options.kind}` + ); + } + const manifestHash = await writeCleanupManifest({ + paths: options.stablePaths, + id: options.id, + kind: options.kind, + entries: await walkTree(options.target, { rejectSymlinks: true }) + }); + cleanup = { kind: options.kind, state: "prepared", manifestHash }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + const manifest = await readCleanupManifest({ + paths: options.stablePaths, + id: options.id, + cleanup + }); + targetExists = await exists3(options.target); + quarantineExists = await exists3(options.quarantine); + if (targetExists && quarantineExists) { + throw new Error(`Native root-move cleanup target and quarantine both exist: ${options.kind}`); + } + if (targetExists) { + if (cleanup.state !== "prepared") { + throw new Error( + `Native root-move cleanup target reappeared after quarantine: ${options.kind}` + ); + } + await quarantineNativeProtectedDirectory({ + root: options.projectRoot, + directory: options.target, + quarantine: options.quarantine, + label: options.label, + beforeQuarantine: options.beforeQuarantine, + afterQuarantine: options.afterQuarantine + }); + quarantineExists = true; + } + if (!quarantineExists) { + if (cleanup.state !== "deleting") { + throw new Error(`Native root-move cleanup quarantine disappeared: ${options.kind}`); + } + return setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id + }); + } + await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest, + exact: cleanup.state !== "deleting" + }); + if (cleanup.state === "prepared") { + cleanup = { ...cleanup, state: "quarantined" }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + if (cleanup.state === "quarantined") { + cleanup = { ...cleanup, state: "deleting" }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + await deleteCleanupManifestSubset({ + projectRoot: options.projectRoot, + quarantine: options.quarantine, + manifest, + hooks: options.hooks + }); + return setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id + }); +} +async function finishForwardMove(options) { + let config = options.config; + let stage = config.native.pending_root_move.stage; + if (stage === "copying") { + if (!await exists3(options.sourcePaths.nativeRoot)) { + throw new Error(`Native source root is missing: ${options.sourcePaths.nativeRoot}`); + } + const stagingRemoval = stagingRemovalDirectory(options.staging); + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: options.pending.id, + kind: "restart-staging", + stablePaths: options.sourcePaths, + target: options.staging, + quarantine: stagingRemoval, + label: "Native root move stale staging removal", + hooks: options.hooks + }); + await walkTree(options.sourcePaths.nativeRoot, { + rejectSymlinks: true, + excludedFiles: /* @__PURE__ */ new Set([path40.resolve(options.lockFile)]) + }); + await copyTree( + options.sourcePaths.nativeRoot, + options.staging, + options.lockFile, + options.destinationPaths.projectRoot + ); + await assertEquivalentTrees(options.sourcePaths.nativeRoot, options.staging, options.lockFile); + config = await setPendingStage({ + projectRoot: options.projectRoot, + config, + pending: options.pending, + stage: "ready" + }); + stage = "ready"; + await options.hooks?.afterRootMoveStage?.("ready", options.journal); + } + if (stage === "ready") { + if (await exists3(options.destinationPaths.nativeRoot)) { + if (await exists3(options.staging)) { + throw new Error(`Native destination is occupied: ${options.destinationPaths.nativeRoot}`); + } + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.destinationPaths.nativeRoot, + options.lockFile + ); + } else { + if (!await exists3(options.staging)) throw new Error(`Native move staging tree is missing`); + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.staging, + options.lockFile + ); + await fs27.rename(options.staging, options.destinationPaths.nativeRoot); + } + config = await setPendingStage({ + projectRoot: options.projectRoot, + config, + pending: options.pending, + stage: "switched", + activeArtifactRoot: options.pending.toArtifactRoot + }); + stage = "switched"; + await options.hooks?.afterRootMoveStage?.("switched", options.journal); + } + if (stage !== "switched") throw new Error(`Unsupported Native root-move stage: ${stage}`); + if (!await exists3(options.destinationPaths.nativeRoot)) { + throw new Error(`Native destination root is missing: ${options.destinationPaths.nativeRoot}`); + } + const sourceRemoval = sourceRemovalDirectory(options.sourcePaths, options.pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(options.sourcePaths.nativeRoot)) { + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.destinationPaths.nativeRoot, + options.lockFile + ); + } + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: options.pending.id, + kind: "forward-source", + stablePaths: options.destinationPaths, + target: options.sourcePaths.nativeRoot, + quarantine: sourceRemoval, + label: "Native root move source removal", + beforeQuarantine: () => options.hooks?.beforeRootMoveSourceRemove?.(options.sourcePaths.nativeRoot), + afterQuarantine: (quarantine) => options.hooks?.afterRootMoveSourceQuarantined?.(quarantine), + hooks: options.hooks + }); + await refreshNativeWorkspaceIdentities(options.destinationPaths); + const destinationJournal = await readNativeTransaction( + options.destinationPaths, + options.pending.id + ); + await finalizeNativeTransaction(options.destinationPaths, destinationJournal, "commit"); + const stableNative = { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot + }; + const committed = { + ...config, + native: { ...stableNative, artifact_root: options.pending.toArtifactRoot } + }; + await writeProjectConfig(options.projectRoot, committed); + return committed; +} +async function moveNativeRoot(options) { + const current = await readProjectConfig(options.projectRoot) ?? defaultProjectConfig("docs"); + if (current.native.pending_root_move) { + throw new Error( + `Native root move ${current.native.pending_root_move.id} is already incomplete` + ); + } + const toArtifactRoot = normalizeArtifactRootRef(options.toArtifactRoot); + if (toArtifactRoot === current.native.artifact_root) { + throw new Error(`Native artifact root is already ${toArtifactRoot}`); + } + const sourcePaths = await nativeProjectPaths(options.projectRoot, current.native.artifact_root); + const destinationPaths = await nativeProjectPaths(options.projectRoot, toArtifactRoot); + if (isInsidePath(sourcePaths.nativeRoot, destinationPaths.nativeRoot) || isInsidePath(destinationPaths.nativeRoot, sourcePaths.nativeRoot)) { + throw new Error("Native source and destination roots must not overlap"); + } + if (!await exists3(sourcePaths.nativeRoot)) { + throw new Error(`Native source root does not exist: ${sourcePaths.nativeRoot}`); + } + await assertNoUnfinishedTransactions(sourcePaths); + if (await exists3(destinationPaths.nativeRoot)) { + throw new Error(`Native destination is occupied: ${destinationPaths.nativeRoot}`); + } + const lock = await acquireNativeLock(sourcePaths, "root-move", `move root to ${toArtifactRoot}`); + const id = randomUUID9(); + const pending = { + id, + fromArtifactRoot: current.native.artifact_root, + toArtifactRoot, + stage: "copying" + }; + const journal = rootMoveJournal({ id, paths: sourcePaths, now: options.now ?? /* @__PURE__ */ new Date() }); + const staging = stagingDirectory(destinationPaths, id); + try { + await assertNoOtherLocks(sourcePaths, lock.file); + if (await exists3(staging)) throw new Error(`Native move staging path is occupied: ${staging}`); + await writeProjectConfig(options.projectRoot, pendingConfig(current, pending)); + await createNativeTransaction(sourcePaths, journal); + await options.hooks?.afterRootMoveStage?.("copying", journal); + await finishForwardMove({ + projectRoot: options.projectRoot, + config: pendingConfig(current, pending), + pending, + sourcePaths, + destinationPaths, + staging, + journal, + lockFile: lock.file, + hooks: options.hooks + }); + return { + fromNativeRoot: sourcePaths.nativeRoot, + toNativeRoot: destinationPaths.nativeRoot, + transactionId: id + }; + } finally { + await releaseNativeLock(lock); + } +} +async function recoverNativeRootMove(options) { + let config = await readProjectConfig(options.projectRoot); + const pending = config?.native.pending_root_move; + if (!config || !pending) throw new Error("No pending Native root move was found"); + const sourcePaths = await nativeProjectPaths(options.projectRoot, pending.fromArtifactRoot); + const destinationPaths = await nativeProjectPaths(options.projectRoot, pending.toArtifactRoot); + const staging = stagingDirectory(destinationPaths, pending.id); + const lockPaths = await exists3(sourcePaths.nativeRoot) ? sourcePaths : destinationPaths; + const lock = await acquireNativeLock(lockPaths, "root-move", `recover root ${pending.id}`); + try { + let journalInfo; + try { + journalInfo = await readRootMoveJournal(sourcePaths, destinationPaths, staging, pending.id); + } catch (error) { + if (pending.stage !== "copying" || !await exists3(sourcePaths.nativeRoot)) throw error; + const journal = rootMoveJournal({ id: pending.id, paths: sourcePaths, now: /* @__PURE__ */ new Date() }); + await createNativeTransaction(sourcePaths, journal); + journalInfo = { journal, paths: sourcePaths }; + } + if (options.strategy === "continue") { + const committed = await finishForwardMove({ + projectRoot: options.projectRoot, + config, + pending, + sourcePaths, + destinationPaths, + staging, + journal: journalInfo.journal, + lockFile: lock.file, + hooks: options.hooks + }); + return { activeNativeRoot: destinationPaths.nativeRoot, config: committed }; + } + if (!await exists3(sourcePaths.nativeRoot)) { + throw new Error("Cannot roll back after the old Native root was removed; continue recovery"); + } + const destinationRemoval = rollbackRemovalDirectory(destinationPaths.nativeRoot, pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(destinationPaths.nativeRoot)) { + await assertEquivalentTrees(sourcePaths.nativeRoot, destinationPaths.nativeRoot, lock.file); + } + if (!config.native.pending_root_move?.cleanup || config.native.pending_root_move.cleanup.kind === "rollback-destination") { + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: pending.id, + kind: "rollback-destination", + stablePaths: sourcePaths, + target: destinationPaths.nativeRoot, + quarantine: destinationRemoval, + label: "Native root move rollback destination removal", + hooks: options.hooks + }); + } + const stagingRemoval = rollbackRemovalDirectory(staging, pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(staging)) { + await assertEquivalentTrees(sourcePaths.nativeRoot, staging, lock.file); + } + if (!config.native.pending_root_move?.cleanup || config.native.pending_root_move.cleanup.kind === "rollback-staging") { + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: pending.id, + kind: "rollback-staging", + stablePaths: sourcePaths, + target: staging, + quarantine: stagingRemoval, + label: "Native root move rollback staging removal", + hooks: options.hooks + }); + } + const sourceJournal = await readNativeTransaction(sourcePaths, pending.id); + await rollbackNativeTransaction(sourcePaths, sourceJournal); + const stableNative = { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot + }; + const restored = { + ...config, + native: { ...stableNative, artifact_root: pending.fromArtifactRoot } + }; + await writeProjectConfig(options.projectRoot, restored); + return { activeNativeRoot: sourcePaths.nativeRoot, config: restored }; + } finally { + await releaseNativeLock(lock); + } +} + +// domains/comet-native/native-doctor.ts +var NATIVE_DOCTOR_MAX_CHANGE_ENTRIES = 4096; +var NATIVE_DOCTOR_MAX_TRANSACTION_ENTRIES = 4096; +var NATIVE_DOCTOR_MAX_LOCK_ENTRIES = 1024; +async function directoryEntries(paths, directory, maxEntries) { + try { + const protectedDirectory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory, + label: "Native doctor directory", + maxEntries + }); + await protectedDirectory.verify(); + return protectedDirectory.entries; + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } +} +async function clearStaleRecoveryLocks(targets, findings) { + const unique = new Map( + targets.map((target) => [ + path41.resolve(target.file), + { ...target, file: path41.resolve(target.file) } + ]) + ); + for (const { paths, file } of unique.values()) { + let diagnosis; + try { + diagnosis = await diagnoseNativeLock(file); + } catch (error) { + findings.push({ + severity: "error", + code: "lock-invalid", + message: `Native recovery lock is invalid: ${error.message}`, + path: file + }); + return false; + } + if (diagnosis.status === "missing") continue; + if (diagnosis.status === "stale") { + const takeover = await takeOverNativeStaleLock(paths, file, diagnosis); + if (takeover.status === "removed") { + findings.push({ + severity: "info", + code: "stale-recovery-lock-removed", + message: `Removed stale lock before explicit transaction recovery`, + path: file + }); + continue; + } + if (takeover.status === "missing") continue; + diagnosis = takeover.diagnosis; + } + if (diagnosis.status === "stale") { + findings.push({ + severity: "error", + code: "lock-takeover-raced", + message: "Native recovery lock changed while doctor was preparing stale takeover", + path: file + }); + return false; + } + findings.push({ + severity: "error", + code: diagnosis.status === "active" ? "lock-active" : "lock-owner-unknown", + message: diagnosis.status === "active" ? `Native recovery lock is still owned by a live process` : `Native recovery lock owner cannot be proven stale`, + path: file + }); + return false; + } + return true; +} +async function inspectSelection(paths, repair) { + const file = nativeSelectionFile(paths); + let value; + try { + await resolveContainedNativePath(paths.projectRoot, file); + const selection = await readNativeSelectionRecord(paths); + if (!selection) return []; + value = selection; + } catch (error) { + if (error.code === "ENOENT") return []; + return [ + { + severity: "error", + code: "selection-invalid", + message: `Native selection is invalid: ${error.message}`, + path: file + } + ]; + } + if (value.schema !== "comet.selection.v2" || value.workflow !== "native" || typeof value.change !== "string") { + return [ + { + severity: "error", + code: "selection-invalid", + message: "Native selection has an invalid schema or change name", + path: file + } + ]; + } + try { + await readNativeChange(paths, value.change); + return []; + } catch (error) { + if (error.code !== "ENOENT") { + return [ + { + severity: "error", + code: "selection-target-invalid", + message: `Selected Native change is invalid: ${error.message}`, + path: file + } + ]; + } + } + if (repair) { + await fs28.rm(file, { force: true }); + return [ + { + severity: "info", + code: "selection-cleared", + message: `Cleared stale Native selection ${value.change}`, + path: file + } + ]; + } + return [ + { + severity: "warning", + code: "selection-stale", + message: `Selected Native change does not exist: ${value.change}`, + path: file + } + ]; +} +async function inspectManagedPaths(paths) { + const findings = []; + for (const managedPath of [ + paths.specsDir, + paths.changesDir, + paths.archiveDir, + paths.runtimeDir, + paths.locksDir, + paths.transactionsDir + ]) { + try { + await resolveContainedNativePath(paths.nativeRoot, managedPath); + } catch (error) { + findings.push({ + severity: "error", + code: "native-path-unsafe", + message: `Managed Native path is unsafe: ${error.message}`, + path: managedPath + }); + } + } + return findings; +} +async function inspectTransactions(paths, options) { + const findings = []; + const unfinished = []; + for (const entry2 of await directoryEntries( + paths, + paths.transactionsDir, + NATIVE_DOCTOR_MAX_TRANSACTION_ENTRIES + )) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let journal; + try { + journal = await readNativeTransaction(paths, entry2.name); + } catch (error) { + findings.push({ + severity: "error", + code: "transaction-invalid", + message: `Native transaction ${entry2.name} is invalid: ${error.message}`, + path: path41.join(paths.transactionsDir, entry2.name) + }); + continue; + } + if (journal.status === "committed" || journal.status === "rolled-back") continue; + if (options.name && journal.change && journal.change !== options.name) continue; + if (journal.kind !== "archive") { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "root-move-transaction-orphaned", + message: `Root-move transaction ${journal.id} is incomplete but project config has no matching pending move` + }); + continue; + } + if (options.repair && options.recoveryStrategy) { + try { + const locksReady = await withNativeLockRecovery( + [paths], + `doctor archive recovery ${journal.id}`, + async () => { + const ready = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, "archive.lock") } + ], + findings + ); + if (!ready) return false; + await recoverArchiveTransaction({ + paths, + transactionId: journal.id, + strategy: options.recoveryStrategy + }); + return true; + } + ); + if (!locksReady) { + unfinished.push(journal); + continue; + } + findings.push({ + severity: "info", + code: "archive-transaction-recovered", + message: `${options.recoveryStrategy === "continue" ? "Continued" : "Rolled back"} archive transaction ${journal.id}` + }); + } catch (error) { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "archive-recovery-failed", + message: `Archive recovery failed: ${error.message}` + }); + } + } else { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "archive-transaction-incomplete", + message: options.repair ? `Archive transaction ${journal.id} needs an explicit recovery strategy` : `Archive transaction ${journal.id} is incomplete`, + repair: options.recoveryStrategy ?? "continue" + }); + } + } + return { findings, unfinished }; +} +async function inspectLocks(paths, repair, unfinished) { + const findings = []; + for (const entry2 of await directoryEntries( + paths, + paths.locksDir, + NATIVE_DOCTOR_MAX_LOCK_ENTRIES + )) { + if (!entry2.isFile() || entry2.isSymbolicLink() || !entry2.name.endsWith(".lock")) continue; + const file = path41.join(paths.locksDir, entry2.name); + try { + const diagnosis = await diagnoseNativeLock(file); + if (diagnosis.status === "active") { + findings.push({ + severity: "warning", + code: "lock-active", + message: `Native lock is active for ${diagnosis.owner?.operation ?? "an operation"}`, + path: file + }); + } else if (diagnosis.status === "unknown") { + findings.push({ + severity: "warning", + code: "lock-owner-unknown", + message: "Native lock owner cannot be proven stale", + path: file + }); + } else if (diagnosis.status === "stale") { + if (repair && unfinished.length === 0) { + const takeover = await takeOverNativeStaleLock(paths, file, diagnosis); + if (takeover.status === "removed") { + findings.push({ + severity: "info", + code: "stale-lock-removed", + message: "Removed a Native lock whose local owner process is absent", + path: file + }); + } else if (takeover.status === "changed") { + findings.push({ + severity: takeover.diagnosis.status === "active" ? "warning" : "error", + code: "lock-takeover-raced", + message: "Native lock changed while doctor was preparing stale takeover", + path: file + }); + } + } else { + findings.push({ + severity: unfinished.length > 0 ? "error" : "warning", + code: "lock-stale", + message: unfinished.length > 0 ? "Native lock is stale but an unfinished transaction still requires recovery" : "Native lock owner process is absent", + path: file + }); + } + } + } catch (error) { + findings.push({ + severity: "error", + code: "lock-invalid", + message: `Native lock metadata is invalid: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function inspectChanges(paths, name) { + const findings = []; + const statuses = name ? await listNativeStatus(paths).then((all) => all.filter((status) => status.name === name)) : await listNativeStatus(paths); + if (name && statuses.length === 0) { + return [ + { + severity: "error", + code: "change-missing", + message: `Native change does not exist: ${name}` + } + ]; + } + for (const status of statuses) { + if (status.migrationRequired) continue; + if (status.phase === "invalid") { + findings.push({ + severity: "error", + code: "change-invalid", + message: status.error ?? `Native change ${status.name} is invalid`, + path: path41.join(paths.changesDir, status.name, NATIVE_CHANGE_STATE_FILE) + }); + continue; + } + const detailed = await inspectNativeStatus(paths, status.name, { details: true }); + for (const artifact of detailed.findings ?? []) { + if (artifact.code === "trajectory-tail-incomplete" || artifact.code === "checkpoint-progress-incomplete") { + continue; + } + findings.push({ + severity: artifact.severity, + code: artifact.code, + message: `${status.name}: ${artifact.message}`, + ...artifact.path ? { path: path41.join(paths.projectRoot, artifact.path) } : {} + }); + } + } + return findings; +} +async function inspectTrajectoryTailRepairs(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + try { + const inspection = await inspectNativeTrajectoryTail(paths, name); + if (inspection.status !== "repairable") continue; + if (!options.repair) { + findings.push({ + severity: "error", + code: "trajectory-tail-incomplete", + message: `Native trajectory for ${name} has an incomplete final line ${inspection.line}; ${inspection.discardedBytes} byte(s) are outside the last complete event`, + path: inspection.file, + repair: "truncate-tail" + }); + continue; + } + const repaired = await repairNativeTrajectoryTail(paths, name); + if (repaired) { + findings.push({ + severity: "info", + code: "trajectory-tail-repaired", + message: `Removed the incomplete Native trajectory tail for ${name} and preserved all complete events`, + path: repaired.file + }); + } + } catch (error) { + findings.push({ + severity: "error", + code: "trajectory-tail-repair-failed", + message: `Native trajectory tail repair failed for ${name}: ${error.message}`, + path: path41.join(paths.changesDir, name, "runtime", "trajectory.jsonl") + }); + } + } + return findings; +} +async function inspectWorkspaceIdentityMigrations(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + try { + if (!await nativeWorkspaceIdentityNeedsMigration(paths, name)) { + continue; + } + if (!options.repair) { + findings.push({ + severity: "warning", + code: "workspace-identity-migration-required", + message: `Native workspace identity for ${name} uses legacy external-probe or hash-only metadata`, + path: nativeWorkspaceFile(paths, name), + repair: "migrate" + }); + continue; + } + const state = await readNativeChange(paths, name); + const migrated = await migrateLegacyNativeWorkspaceIdentity({ + paths, + name, + revision: state.revision + }); + if (migrated) { + findings.push({ + severity: "info", + code: "workspace-identity-migrated", + message: `Replaced legacy workspace metadata for ${name} with process-free root identities`, + path: nativeWorkspaceFile(paths, name) + }); + } + } catch (error) { + findings.push({ + severity: "error", + code: "workspace-identity-migration-failed", + message: `Native workspace identity migration failed for ${name}: ${error.message}`, + path: nativeWorkspaceFile(paths, name) + }); + } + } + return findings; +} +async function inspectSchemaMigrations(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + const file = nativeSchemaMigrationJournalFile(paths, name); + try { + const pending = await inspectPendingNativeSchemaMigration(paths, name); + const inspection = await inspectNativeChange(paths, name); + if (!pending && inspection.status === "current") continue; + if (inspection.status === "runtime-incompatible") { + findings.push({ + severity: "error", + code: "change-runtime-incompatible", + message: inspection.message ?? `Native change ${name} requires a newer runtime`, + path: path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE) + }); + continue; + } + if (!options.repair) { + findings.push({ + severity: "error", + code: pending ? "schema-migration-incomplete" : "schema-migration-required", + message: pending ? `Native schema migration ${pending.id} is incomplete for ${name}` : `Native change ${name} requires migration to the current schema`, + path: pending ? file : path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE), + repair: "migrate" + }); + continue; + } + await migrateNativeChange({ paths, name }); + findings.push({ + severity: "info", + code: pending ? "schema-migration-recovered" : "schema-migrated", + message: `Migrated Native change ${name} to the current schema`, + path: path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE) + }); + } catch (error) { + if (error.code === "ENOENT") continue; + findings.push({ + severity: "error", + code: "schema-migration-failed", + message: `Native schema migration failed for ${name}: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function inspectTransitionJournals(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + let journal; + try { + journal = await inspectPendingNativeTransition(paths, name); + } catch (error) { + if (error instanceof NativeTransitionMigrationRequiredError) continue; + findings.push({ + severity: "error", + code: "transition-invalid", + message: `Native transition journal is invalid: ${error.message}`, + path: nativeTransitionJournalFile(paths, name) + }); + continue; + } + if (!journal) continue; + if (options.repair && options.recoveryStrategy === "continue") { + try { + const recovered = await withNativeLockRecovery( + [paths], + `doctor transition recovery ${name}`, + async () => { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, `transition-${name}.lock`) } + ], + findings + ); + if (!locksReady) return false; + await continueNativeTransition(paths, name); + return true; + } + ); + if (!recovered) continue; + findings.push({ + severity: "info", + code: "transition-recovered", + message: `Continued Native phase transition ${journal.id} for ${name}`, + path: nativeTransitionJournalFile(paths, name) + }); + } catch (error) { + findings.push({ + severity: "error", + code: "transition-recovery-failed", + message: `Native transition recovery failed: ${error.message}`, + path: nativeTransitionJournalFile(paths, name) + }); + } + continue; + } + findings.push({ + severity: "error", + code: "transition-incomplete", + message: options.repair && options.recoveryStrategy === "rollback" ? `Native phase transition ${journal.id} only supports deterministic continue recovery` : `Native phase transition ${journal.id} is incomplete for ${name}`, + path: nativeTransitionJournalFile(paths, name), + repair: "continue" + }); + } + return findings; +} +async function inspectCheckpointJournals(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + const file = nativeCheckpointJournalFile(paths, name); + let journal; + try { + journal = await readNativeCheckpointJournal(paths, name); + } catch (error) { + if (error.code === "ENOENT") continue; + findings.push({ + severity: "error", + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint journal is invalid: ${error.message}`, + path: file + }); + continue; + } + if (!journal) continue; + if (!options.repair) { + findings.push({ + severity: "error", + code: "checkpoint-progress-incomplete", + message: `Native progress checkpoint ${journal.id} is incomplete for ${name}`, + path: file + }); + continue; + } + try { + const recovered = await withNativeLockRecovery( + [paths], + `doctor checkpoint recovery ${name}`, + async () => { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, `transition-${name}.lock`) } + ], + findings + ); + if (!locksReady) return false; + await continueNativeCheckpoint(paths, name); + return true; + } + ); + if (!recovered) continue; + findings.push({ + severity: "info", + code: "checkpoint-progress-recovered", + message: `Continued Native progress checkpoint ${journal.id} for ${name}`, + path: file + }); + } catch (error) { + findings.push({ + severity: "error", + code: "checkpoint-progress-recovery-failed", + message: `Native progress checkpoint recovery failed: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function doctorNativeProject(options) { + const repair = options.repair ?? false; + const findings = []; + let paths = options.paths; + let config; + try { + config = await readProjectConfig(paths.projectRoot); + } catch (error) { + const result2 = { + healthy: false, + findings: [ + { + severity: "error", + code: "config-invalid", + message: `Comet project config is invalid: ${error.message}`, + path: paths.configFile + } + ] + }; + return result2; + } + let relocationRecoveryPending = Boolean(config?.native.pending_root_move); + if (config?.native.pending_root_move) { + const pending = config.native.pending_root_move; + const [fromPaths, toPaths] = await Promise.all([ + nativeProjectPaths(paths.projectRoot, pending.fromArtifactRoot), + nativeProjectPaths(paths.projectRoot, pending.toArtifactRoot) + ]); + if (repair && options.recoveryStrategy) { + try { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths: fromPaths, file: path41.join(fromPaths.locksDir, "root-move.lock") }, + { paths: toPaths, file: path41.join(toPaths.locksDir, "root-move.lock") } + ], + findings + ); + if (!locksReady) return { healthy: false, findings }; + const recovered = await recoverNativeRootMove({ + projectRoot: paths.projectRoot, + strategy: options.recoveryStrategy + }); + paths = await nativeProjectPaths(paths.projectRoot, recovered.config.native.artifact_root); + relocationRecoveryPending = false; + findings.push({ + severity: "info", + code: "root-move-recovered", + message: `${options.recoveryStrategy === "continue" ? "Continued" : "Rolled back"} Native root move ${pending.id}` + }); + } catch (error) { + findings.push({ + severity: "error", + code: "root-move-recovery-failed", + message: `Native root recovery failed: ${error.message}` + }); + return { healthy: false, findings }; + } + } else { + findings.push({ + severity: "error", + code: "root-move-incomplete", + message: `Native root move ${pending.id} is ${pending.stage}; inspect ${fromPaths.nativeRoot} and ${toPaths.nativeRoot}`, + repair: options.recoveryStrategy ?? "continue" + }); + } + } + const managedPathFindings = await inspectManagedPaths(paths); + findings.push(...managedPathFindings); + if (managedPathFindings.length > 0) return { healthy: false, findings }; + const transactions = await inspectTransactions(paths, { + name: options.name, + repair, + recoveryStrategy: options.recoveryStrategy + }); + findings.push(...transactions.findings); + findings.push(...await inspectSchemaMigrations(paths, { name: options.name, repair })); + findings.push( + ...await inspectWorkspaceIdentityMigrations(paths, { name: options.name, repair }) + ); + findings.push(...await inspectTrajectoryTailRepairs(paths, { name: options.name, repair })); + findings.push( + ...await inspectTransitionJournals(paths, { + name: options.name, + repair, + recoveryStrategy: options.recoveryStrategy + }) + ); + findings.push(...await inspectCheckpointJournals(paths, { name: options.name, repair })); + findings.push( + ...await inspectNativeEvidenceRetention({ + paths, + name: options.name, + repair, + now: options.now, + deferAll: relocationRecoveryPending || transactions.unfinished.length > 0 + }) + ); + findings.push(...await inspectLocks(paths, repair, transactions.unfinished)); + findings.push(...await inspectSelection(paths, repair)); + findings.push(...await inspectChanges(paths, options.name)); + return { + healthy: findings.every((finding) => finding.severity === "info"), + findings + }; +} + +// domains/comet-native/native-check-receipt.ts +import { createHash as createHash15 } from "node:crypto"; +import { constants as fsConstants6, promises as fs29 } from "node:fs"; +import path42 from "node:path"; +import { TextDecoder as TextDecoder5 } from "node:util"; +var ISSUE_KIND_RANK = { + "conflict-marker": 0, + "trailing-whitespace": 1, + "space-before-tab": 2, + "scope-mismatch": 3, + "unsafe-file": 4, + "scan-limit": 5 +}; +var ScopedFileError = class extends Error { + constructor(kind, message) { + super(message); + this.kind = kind; + } + kind; +}; +function projectionManifest2(projection) { + return { + schema: "comet.native.content-snapshot.v1", + origin: projection.origin, + ...projection.capture ? { capture: projection.capture } : {}, + createdAt: "1970-01-01T00:00:00.000Z", + complete: projection.complete, + limits: projection.limits, + ...projection.policy ? { policy: projection.policy } : {}, + entries: projection.entries, + omitted: projection.omitted, + omittedCount: projection.omittedCount, + ...projection.omissionOverflow ? { omissionOverflow: projection.omissionOverflow } : {} + }; +} +async function collectBoundFacts(options) { + const baseline = projectionManifest2(options.scope.baseline); + const [contract, snapshot2] = await Promise.all([ + collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }), + createNativeCurrentContentSnapshot(options.paths, baseline, { origin: "explicit" }) + ]); + const currentBundle = buildNativeImplementationScopeBundle({ + baseline, + current: snapshot2, + contractHash: options.scope.authority.contractHash, + declaredArtifacts: options.scope.authority.declaredArtifacts, + noCodeReason: options.scope.authority.noCodeReason, + ...options.scope.authority.gitChangedPaths ? { gitChangedPaths: options.scope.authority.gitChangedPaths } : {} + }); + return { + contractHash: contract.contract.contractHash, + snapshotHash: currentBundle.scope.currentProjectionHash + }; +} +function receiptTime(clock, label) { + const value = (clock ?? (() => /* @__PURE__ */ new Date()))(); + if (!(value instanceof Date) || !Number.isFinite(value.getTime())) { + throw new Error(`Native check ${label} time is invalid`); + } + return value.toISOString(); +} +function staleReasons(before, after, scope) { + const reasons = []; + const expectedContractHash = scope.scope.contractHash; + const expectedSnapshotHash = scope.scope.currentProjectionHash; + if (before.contractHash !== expectedContractHash) { + reasons.push("contract-before-does-not-match-scope"); + } + if (before.snapshotHash !== expectedSnapshotHash) { + reasons.push("implementation-before-does-not-match-scope"); + } + if (after.contractHash !== before.contractHash) { + reasons.push("contract-changed-during-check"); + } + if (after.snapshotHash !== before.snapshotHash) { + reasons.push("implementation-changed-during-check"); + } + if (after.contractHash === before.contractHash && after.contractHash !== expectedContractHash) { + reasons.push("contract-after-does-not-match-scope"); + } + if (after.snapshotHash === before.snapshotHash && after.snapshotHash !== expectedSnapshotHash) { + reasons.push("implementation-after-does-not-match-scope"); + } + return reasons; +} +function sameDirectoryIdentity7(identity, stat) { + const stableMetadata = identity.birthtimeMs === stat.birthtimeMs && identity.ctimeMs === stat.ctimeMs; + return stableMetadata && sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity8(left, right) { + const stableMetadata = left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; + return stableMetadata && sameFileObject( + { ...left, birthtime: left.birthtimeMs }, + { + ...right, + birthtime: right.birthtimeMs + } + ); +} +async function captureProjectDirectoryChain(projectRoot, directory) { + const lexicalRoot = path42.resolve(projectRoot); + const lexicalDirectory = path42.resolve(directory); + if (!isInsidePath(lexicalRoot, lexicalDirectory)) { + throw new ScopedFileError("unsafe-file", "Scoped file parent escapes the project root"); + } + const chain = []; + let cursor = lexicalRoot; + for (const segment of [ + "", + ...path42.relative(lexicalRoot, lexicalDirectory).split(path42.sep).filter(Boolean) + ]) { + if (segment) cursor = path42.join(cursor, segment); + let stat; + try { + stat = await fs29.lstat(cursor); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file parent no longer exists"); + } + throw error; + } + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new ScopedFileError("unsafe-file", "Scoped file parent is not a real directory"); + } + const realPath = await fs29.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file parent resolves outside the project"); + } + chain.push({ + path: cursor, + realPath, + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs + }); + } + return { physicalRoot: chain[0].realPath, chain }; +} +async function verifyProjectDirectoryChain(chain) { + for (const identity of chain) { + let stat; + try { + stat = await fs29.lstat(identity.path); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("unsafe-file", "Scoped file parent changed while reading"); + } + throw error; + } + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity7(identity, stat) || await fs29.realpath(identity.path) !== identity.realPath) { + throw new ScopedFileError("unsafe-file", "Scoped file parent changed while reading"); + } + } +} +async function readScopedFile(options) { + const lexicalFile = path42.resolve(options.projectRoot, ...options.file.path.split("/")); + if (!isInsidePath(options.projectRoot, lexicalFile)) { + throw new ScopedFileError("unsafe-file", "Scoped file path escapes the project root"); + } + const { physicalRoot, chain } = await captureProjectDirectoryChain( + options.projectRoot, + path42.dirname(lexicalFile) + ); + let before; + try { + before = await fs29.lstat(lexicalFile); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file no longer exists"); + } + throw error; + } + if (!before.isFile() || before.isSymbolicLink()) { + throw new ScopedFileError("unsafe-file", "Scoped file is not a real regular file"); + } + if (before.size !== options.file.expected.size) { + throw new ScopedFileError( + "scope-mismatch", + "Scoped file size no longer matches its projection" + ); + } + const beforeRealPath = await fs29.realpath(lexicalFile); + if (!isInsidePath(physicalRoot, beforeRealPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file resolves outside the project root"); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants6.O_RDONLY | fsConstants6.O_NOFOLLOW | fsConstants6.O_NONBLOCK; + const handle = await fs29.open(lexicalFile, openFlags).catch((error) => { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file no longer exists"); + } + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new ScopedFileError("unsafe-file", "Scoped file became unsafe while opening"); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs29.lstat(lexicalFile), + fs29.realpath(lexicalFile) + ]); + await verifyProjectDirectoryChain(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity8(before, opened) || !sameFileIdentity8(opened, pathAfterOpen)) { + throw new ScopedFileError("unsafe-file", "Scoped file changed while opening"); + } + const chunks = []; + const digest = createHash15("sha256"); + const buffer = Buffer.allocUnsafe(64 * 1024); + let total = 0; + while (true) { + const remaining = options.file.expected.size + 1 - total; + if (remaining < 1) { + throw new ScopedFileError("scope-mismatch", "Scoped file exceeds its projected size"); + } + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > options.file.expected.size) { + throw new ScopedFileError("scope-mismatch", "Scoped file exceeds its projected size"); + } + const chunk = Buffer.from(buffer.subarray(0, read.bytesRead)); + chunks.push(chunk); + digest.update(chunk); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs29.lstat(lexicalFile), + fs29.realpath(lexicalFile) + ]); + await verifyProjectDirectoryChain(chain); + if (total !== options.file.expected.size || digest.digest("hex") !== options.file.expected.hash) { + throw new ScopedFileError( + "scope-mismatch", + "Scoped file content no longer matches its projection" + ); + } + if (!afterHandle.isFile() || !afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !isInsidePath(physicalRoot, afterRealPath) || !sameFileIdentity8(opened, afterHandle) || !sameFileIdentity8(opened, afterPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file changed while reading"); + } + const content = Buffer.concat(chunks, total); + if (content.includes(0)) return { bytes: total, text: null }; + try { + return { bytes: total, text: new TextDecoder5("utf-8", { fatal: true }).decode(content) }; + } catch { + return { bytes: total, text: null }; + } + } finally { + await handle.close(); + } +} +function selectedScopedFiles(scope) { + const current = new Map(scope.current.entries.map((entry2) => [entry2.path, entry2])); + const files = []; + const mismatches = []; + for (const change of scope.scope.changes) { + if (!change.after) continue; + const entry2 = current.get(change.path); + if (!entry2 || entry2.hash !== change.after.hash || entry2.size !== change.after.size || entry2.type !== "file") { + mismatches.push(change.path); + continue; + } + files.push({ path: change.path, expected: change.after }); + } + files.sort((left, right) => left.path.localeCompare(right.path, "en")); + mismatches.sort((left, right) => left.localeCompare(right, "en")); + return { files, mismatches }; +} +function inspectText(pathRef, text, addIssue) { + const lines = text.split("\n"); + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index].endsWith("\r") ? lines[index].slice(0, -1) : lines[index]; + const lineNumber = index + 1; + if (/^(?:<{7}|={7}|>{7})(?: |$)/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "conflict-marker" }); + } + if (/[ \t]+$/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "trailing-whitespace" }); + } + if (/^ +\t/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "space-before-tab" }); + } + } +} +async function executeNativeCheckReceipt(options) { + if (!options.state.implementation_scope) { + throw new Error("Native scoped check requires an implementation scope"); + } + const scope = await readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ); + const before = await collectBoundFacts({ paths: options.paths, state: options.state, scope }); + const startedAt = receiptTime(options.clock, "start"); + const selected = selectedScopedFiles(scope); + const issues = []; + let issueCount = 0; + const addIssue = (issue) => { + issueCount += 1; + if (issues.length < NATIVE_CHECK_LIMITS.maxIssues) issues.push(issue); + }; + let candidates = selected.files; + const selectedCount = selected.files.length + selected.mismatches.length; + const scopeDetailOverflow2 = scope.scope.unresolvedScopes.some( + (unresolved) => unresolved.kind === "scope-detail-overflow" + ); + if (scopeDetailOverflow2) { + const overflowPath = scope.scope.changes.at(-1)?.path ?? scope.current.entries.at(-1)?.path ?? scope.baseline.entries.at(-1)?.path; + if (!overflowPath) { + throw new Error("Native implementation scope overflow has no bounded diagnostic path"); + } + addIssue({ path: overflowPath, line: 1, kind: "scan-limit" }); + candidates = []; + } else if (selectedCount > NATIVE_CHECK_LIMITS.maxFiles) { + const selectedPaths = [...selected.files.map((file) => file.path), ...selected.mismatches].sort( + (left, right) => left.localeCompare(right, "en") + ); + addIssue({ + path: selectedPaths[NATIVE_CHECK_LIMITS.maxFiles], + line: 1, + kind: "scan-limit" + }); + candidates = []; + } else { + let total = 0; + for (const file of selected.files) { + if (file.expected.size > NATIVE_CHECK_LIMITS.maxFileBytes) { + addIssue({ path: file.path, line: 1, kind: "scan-limit" }); + candidates = []; + break; + } + total += file.expected.size; + if (total > NATIVE_CHECK_LIMITS.maxTotalBytes) { + addIssue({ path: file.path, line: 1, kind: "scan-limit" }); + candidates = []; + break; + } + } + } + for (const mismatch of selected.mismatches) { + addIssue({ path: mismatch, line: 1, kind: "scope-mismatch" }); + } + let filesScanned = 0; + let binaryFilesSkipped = 0; + let bytesScanned = 0; + for (const file of candidates) { + try { + const scanned = await readScopedFile({ projectRoot: options.paths.projectRoot, file }); + bytesScanned += scanned.bytes; + if (scanned.text === null) { + binaryFilesSkipped += 1; + } else { + filesScanned += 1; + inspectText(file.path, scanned.text, addIssue); + } + } catch (error) { + if (!(error instanceof ScopedFileError)) throw error; + addIssue({ path: file.path, line: 1, kind: error.kind }); + } + } + const after = await collectBoundFacts({ paths: options.paths, state: options.state, scope }); + const endedAt = receiptTime(options.clock, "end"); + const reasons = staleReasons(before, after, scope); + issues.sort( + (left, right) => left.path.localeCompare(right.path, "en") || left.line - right.line || ISSUE_KIND_RANK[left.kind] - ISSUE_KIND_RANK[right.kind] + ); + const receipt = buildNativeCheckReceipt({ + change: options.state.name, + sourceRevision: options.state.revision, + status: issueCount === 0 && reasons.length === 0 ? "passed" : "failed", + startedAt, + endedAt, + contract: { + expectedHash: scope.scope.contractHash, + beforeHash: before.contractHash, + afterHash: after.contractHash + }, + implementation: { + scopeHash: scope.scope.scopeHash, + expectedSnapshotHash: scope.scope.currentProjectionHash, + beforeSnapshotHash: before.snapshotHash, + afterSnapshotHash: after.snapshotHash + }, + counts: { + filesSelected: selectedCount, + filesScanned, + binaryFilesSkipped, + bytesScanned, + issueCount, + recordedIssueCount: issues.length + }, + issues, + issuesTruncated: issueCount > issues.length, + stale: reasons.length > 0, + staleReasons: reasons + }); + const ref = await writeNativeCheckReceipt({ + paths: options.paths, + name: options.state.name, + receipt + }); + return { receipt: parseNativeCheckReceipt(receipt), ref }; +} + +// domains/comet-native/native-check.ts +async function checkNativeChange(options) { + return withNativeMutationLock( + options.paths, + `check ${options.name}`, + () => withNativeTransitionLock(options.paths, options.name, `check ${options.name}`, async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + if (state.phase !== "verify") { + throw new Error(`Native check requires Verify, got ${state.phase}`); + } + if (!state.implementation_scope) { + throw new Error("Native check requires an implementation scope"); + } + const executed = await executeNativeCheckReceipt({ + paths: options.paths, + state + }); + return { change: state, receipt: executed.receipt, ref: executed.ref }; + }) + ); +} + +// domains/comet-native/native-progress-checkpoint.ts +import { randomUUID as randomUUID10 } from "crypto"; +function requiredText2(value, label) { + const normalized = redactNativeCredentialText(value).trim(); + if (normalized.length === 0 || normalized.length > 2e3) { + throw new Error(`${label} must be between 1 and 2000 characters`); + } + return normalized; +} +function expectedRevisionValue(value) { + if (value === void 0) return void 0; + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native expected revision must be a positive integer"); + } + return value; +} +async function checkpointNativeChange(options) { + const summary = requiredText2(options.summary, "Checkpoint summary"); + const nextAction = requiredText2(options.nextAction, "Checkpoint next action"); + const expectedRevision = expectedRevisionValue(options.expectedRevision); + return withNativeMutationLock( + options.paths, + `checkpoint ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `checkpoint ${options.name}`, + async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + const manifest = await createNativeCheckpointManifest( + options.paths, + options.name, + options.artifacts ?? [] + ); + const manifestHash = hashNativeCheckpointManifest(manifest); + const inputHash = sha256Text( + JSON.stringify({ + summary, + nextAction, + artifacts: manifest.artifacts + }) + ); + const existing = await readNativeProgressCheckpoint(options.paths, options.name); + if (existing?.inputHash === inputHash && existing.stateRevision === state.revision && existing.phase === state.phase) { + if (expectedRevision !== void 0 && expectedRevision !== existing.previousRevision && expectedRevision !== state.revision) { + throw new NativeChangeRevisionConflictError( + state.name, + expectedRevision, + state.revision + ); + } + return { + change: state, + checkpoint: existing, + idempotent: true, + expectedRevision: expectedRevision ?? existing.previousRevision, + previousRevision: existing.previousRevision, + revision: state.revision, + outcome: "idempotent", + continuation: nativeContinuation({ state }) + }; + } + if (expectedRevision !== void 0 && state.revision !== expectedRevision) { + throw new NativeChangeRevisionConflictError(state.name, expectedRevision, state.revision); + } + const nextState = { ...state, revision: state.revision + 1 }; + const checkpoint = { + schema: "comet.native.progress-checkpoint.v1", + id: options.checkpointId?.() ?? randomUUID10(), + change: state.name, + phase: state.phase, + previousRevision: state.revision, + stateRevision: nextState.revision, + summary, + nextAction, + inputHash, + manifestHash, + manifestRef: nativeCheckpointManifestRef(manifestHash), + artifactCount: manifest.artifacts.length, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString() + }; + const journal = await prepareNativeCheckpointJournal({ + paths: options.paths, + previousState: state, + nextState, + checkpoint, + manifest, + now: options.now, + checkpointId: () => checkpoint.id + }); + await options.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeCheckpointLocked( + options.paths, + options.name, + options.hooks + ); + if (!persisted) throw new Error("Native checkpoint journal disappeared before completion"); + return { + change: persisted.nextState, + checkpoint: persisted.checkpoint, + idempotent: false, + expectedRevision: expectedRevision ?? persisted.checkpoint.previousRevision, + previousRevision: persisted.checkpoint.previousRevision, + revision: persisted.nextState.revision, + outcome: "recorded", + continuation: nativeContinuation({ state: persisted.nextState }) + }; + } + ) + ); +} + +// domains/comet-native/native-specs.ts +import { promises as fs30 } from "fs"; +import path43 from "path"; +var MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES = NATIVE_CONTRACT_FILE_LIMITS.maxSpecs * 4; +async function optionalHash3(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function proposedCapabilities(paths, name) { + const specsDir = path43.join(nativeChangeDir(paths, name), "specs"); + let guard; + try { + guard = await captureNativeProtectedDirectoryGuard({ + root: paths.nativeRoot, + directory: specsDir, + label: "Native proposed specs directory" + }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const capabilities = []; + let entryCount = 0; + const directory = await fs30.opendir(specsDir); + try { + for await (const entry2 of directory) { + entryCount += 1; + if (entryCount > MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES) { + throw new Error( + `Native proposed specs directory exceeds ${MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES} entries` + ); + } + if (entry2.isSymbolicLink()) { + throw new Error(`Proposed spec capability must not be a symbolic link: ${entry2.name}`); + } + if (!entry2.isDirectory()) continue; + assertNativeName(entry2.name); + const source = path43.join(specsDir, entry2.name, "spec.md"); + await resolveContainedNativePath(paths.nativeRoot, source); + let stat; + try { + stat = await fs30.lstat(source); + } catch (error) { + if (error.code === "ENOENT") continue; + throw error; + } + if (stat.isSymbolicLink() || !stat.isFile()) { + throw new Error(`Proposed spec must be a regular file: ${entry2.name}`); + } + capabilities.push(entry2.name); + if (capabilities.length > NATIVE_CONTRACT_FILE_LIMITS.maxSpecs) { + throw new Error("Native proposed specs exceed the spec-count budget"); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + await guard.verify(); + return capabilities.sort(); +} +async function reconcileNativeSpecChanges(paths, state) { + const previous = new Map(state.spec_changes.map((change) => [change.capability, change])); + const proposed = await proposedCapabilities(paths, state.name); + const changes = []; + for (const capability of proposed) { + const existing = previous.get(capability); + if (existing?.operation === "remove") { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + if (existing) { + changes.push({ + ...existing, + source: `specs/${capability}/spec.md` + }); + continue; + } + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + changes.push({ + capability, + operation: baseHash === null ? "create" : "replace", + source: `specs/${capability}/spec.md`, + base_hash: baseHash + }); + } + for (const change of state.spec_changes) { + if (change.operation === "remove" && !proposed.includes(change.capability)) { + changes.push(change); + } + } + return changes.sort((left, right) => left.capability.localeCompare(right.capability)); +} +async function refreshNativeSpecChanges(paths, state) { + const proposed = await proposedCapabilities(paths, state.name); + const changes = []; + for (const capability of proposed) { + const existing = state.spec_changes.find((change) => change.capability === capability); + if (existing?.operation === "remove") { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + changes.push({ + capability, + operation: baseHash === null ? "create" : "replace", + source: `specs/${capability}/spec.md`, + base_hash: baseHash + }); + } + for (const change of state.spec_changes) { + if (change.operation !== "remove" || proposed.includes(change.capability)) continue; + const canonical = canonicalSpecPath(paths, change.capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + if (baseHash !== null) { + changes.push({ ...change, base_hash: baseHash }); + } + } + return changes.sort((left, right) => left.capability.localeCompare(right.capability)); +} +async function rebaseNativeSpecChanges(options) { + assertNativeName(options.name); + const summary = redactNativeCredentialText(options.summary); + assertNativeTrajectoryText(summary, "Spec rebase summary"); + return withNativeMutationLock( + options.paths, + `rebase specs for ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `rebase specs for ${options.name}`, + async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + if (state.phase === "shape") { + throw new Error("Shape spec metadata is refreshed by the next command"); + } + if (state.archived) throw new Error(`Native change ${state.name} is already archived`); + const changeDir = nativeChangeDir(options.paths, options.name); + const run = await readNativeRunState(changeDir); + if (!run || run.runId !== state.run_id || run.currentStep !== state.phase || run.pending) { + throw new Error(`Native Run state is missing or inconsistent for ${state.name}`); + } + const specChanges = await refreshNativeSpecChanges(options.paths, state); + const nextState = { + ...state, + revision: state.revision + 1, + phase: "build", + spec_changes: specChanges, + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + }; + const nextRun = { + ...run, + currentStep: "build", + iteration: run.iteration + 1, + pending: null, + status: "running" + }; + const evidenceHash = sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: state.name, + summary, + specChanges + }) + ); + await prepareNativeTransition({ + paths: options.paths, + previousState: state, + nextState, + previousRun: run, + nextRun, + evidenceHash, + eventData: { + previousPhase: state.phase, + nextPhase: "build", + evidenceHash, + summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }, + operation: "spec-rebase", + now: options.now, + transitionId: options.transitionId + }); + const rebased = await continueNativeTransitionLocked(options.paths, options.name); + if (!rebased) throw new Error("Native spec rebase journal disappeared before completion"); + return rebased; + } + ) + ); +} +async function markNativeSpecRemoval(paths, name, capability) { + assertNativeName(name); + assertNativeName(capability); + return withNativeMutationLock( + paths, + `remove spec ${capability} from ${name}`, + () => withNativeTransitionLock(paths, name, `remove spec ${capability} from ${name}`, async () => { + await settleNativeChangeJournalsLocked(paths, name); + return markNativeSpecRemovalLocked(paths, name, capability); + }) + ); +} +async function markNativeSpecRemovalLocked(paths, name, capability) { + const state = await readNativeChange(paths, name); + if (state.phase === "archive" || state.archived) { + throw new Error(`Native change ${name} no longer accepts spec changes`); + } + const proposed = await proposedCapabilities(paths, name); + if (proposed.includes(capability)) { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + const previous = state.spec_changes.find((change) => change.capability === capability); + if (previous?.operation === "remove") return state; + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + if (baseHash === null) throw new Error(`Canonical spec is missing: ${capability}`); + const updated = { + ...state, + spec_changes: [ + ...state.spec_changes.filter((change) => change.capability !== capability), + { capability, operation: "remove", base_hash: baseHash } + ].sort((left, right) => left.capability.localeCompare(right.capability)) + }; + await compareAndSwapNativeChangeLocked(paths, updated, state.revision); + return updated; +} +async function readNativeProposedSpecs(paths, name) { + const changeDir = nativeChangeDir(paths, name); + const result2 = {}; + let totalBytes = 0; + for (const capability of await proposedCapabilities(paths, name)) { + const source = await readNativeBoundedTextFile({ + root: changeDir, + ref: `specs/${capability}/spec.md`, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + totalBytes += source.size; + if (totalBytes > NATIVE_CONTRACT_FILE_LIMITS.maxTotalBytes) { + throw new Error("Native proposed specs exceed the total byte budget"); + } + result2[capability] = source.text; + } + return result2; +} + +// domains/comet-native/native-transitions.ts +import { randomUUID as randomUUID11 } from "crypto"; + +// domains/comet-native/native-guards.ts +import { promises as fs31 } from "fs"; +import path44 from "path"; +function validation(findings) { + return { valid: findings.length === 0, findings }; +} +async function validateBuildArtifacts(paths, evidence) { + const findings = []; + if ((evidence.noCodeReason ?? "").trim().length > 0) return findings; + if (!evidence.artifacts || evidence.artifacts.length === 0) { + return [ + { + code: "build-evidence-missing", + message: "Build requires an artifact reference or an explicit no-code reason" + } + ]; + } + for (const artifact of evidence.artifacts) { + if (path44.isAbsolute(artifact) || artifact.split(/[\\/]/u).includes("..") || /^(?:[A-Za-z]:|~|[\\/])/u.test(artifact)) { + findings.push({ + code: "build-artifact-unsafe", + message: `Unsafe build artifact: ${artifact}` + }); + continue; + } + const target = path44.resolve(paths.projectRoot, ...artifact.split(/[\\/]/u)); + if (!isInsidePath(paths.projectRoot, target)) { + findings.push({ + code: "build-artifact-unsafe", + message: `Unsafe build artifact: ${artifact}` + }); + continue; + } + try { + await fs31.access(target); + } catch { + findings.push({ + code: "build-artifact-missing", + message: `Build artifact does not exist: ${artifact}`, + path: artifact + }); + } + } + return findings; +} +async function inspectNativeGuard(options) { + const findings = []; + const changeDir = nativeChangeDir(options.paths, options.state.name); + if (options.evidence.summary.trim().length === 0) { + findings.push({ + code: "transition-summary-missing", + message: "Phase transition requires a summary" + }); + } + if (options.evidence.confirmed && options.state.phase !== "shape" && options.state.phase !== "build") { + findings.push({ + code: "confirmation-not-shape", + message: "Explicit confirmation is only valid while leaving Shape or Build" + }); + } + findings.push(...await inspectNativeRunConsistency(options.paths, options.state)); + if (options.state.phase === "shape" || options.state.phase === "build") { + const capturedBaseline = await readNativeBaselineManifest(options.paths, options.state.name); + if (capturedBaseline === null) { + findings.push({ + code: "baseline-snapshot-missing", + message: "Native baseline is missing; restore a trusted baseline before advancing" + }); + } else { + const baseline = await filterNativeContentSnapshotToProjectScope( + options.paths, + capturedBaseline + ); + if (!baseline.complete) { + findings.push({ + code: "baseline-snapshot-incomplete", + message: `Native baseline is incomplete within the project-owned scope (${baseline.omittedCount} omitted entries); resolve the omissions before advancing` + }); + } + } + } + if (options.state.phase === "shape") { + const brief = await validateNativeBrief(changeDir, options.state.brief); + const specs = await validateNativeSpecChanges(options.paths, options.state); + findings.push(...brief.findings, ...specs.findings); + if (findings.length === 0 && !options.evidence.confirmed) { + findings.push({ + code: "shape-confirmation-required", + message: "Native clarification requires explicit user confirmation of the shared understanding before Build" + }); + } + } else if (options.state.phase === "build") { + findings.push( + ...(await validateNativeBrief(changeDir, options.state.brief)).findings, + ...(await validateNativeSpecChanges(options.paths, options.state)).findings + ); + findings.push(...await validateBuildArtifacts(options.paths, options.evidence)); + if (findings.length === 0 && options.state.approval !== "confirmed" && !options.evidence.confirmed) { + findings.push({ + code: "approval-confirmation-required", + message: "Native approval is implicit; confirm the current shared understanding before leaving Build" + }); + } + } else if (options.state.phase === "verify") { + const report = options.evidence.verificationReport ?? options.state.verification_report; + if (!report) { + findings.push({ + code: "verification-report-missing", + message: "Verify requires a report path" + }); + } else { + findings.push(...(await validateNativeVerification(changeDir, report)).findings); + } + if (!options.evidence.verificationResult) { + findings.push({ + code: "verification-result-missing", + message: "Verify requires pass or fail" + }); + } + findings.push(...(await validateNativeSpecChanges(options.paths, options.state)).findings); + } else { + findings.push({ + code: "archive-command-required", + message: "Use comet native archive for the Archive phase" + }); + } + return validation(findings); +} + +// domains/comet-native/native-build-evidence.ts +import { promises as fs32 } from "node:fs"; +import path45 from "node:path"; +var NATIVE_BUILD_EVIDENCE_LIMITS = { + maxDeclaredArtifacts: 128, + maxArtifactPathBytes: 512 +}; +function assertStableNativeSelection(snapshot2, source) { + if (snapshot2.omitted.some((omission) => omission.reason === "git-selection-changed")) { + throw new Error( + `Native Git selection changed while capturing the ${source}; stabilize the Git index and retry Build evidence` + ); + } + if (snapshot2.omitted.some( + (omission) => omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed" + )) { + throw new Error( + `Native physical selection was incomplete or changed while capturing the ${source}; retry Build evidence with a stable bounded project tree` + ); + } +} +function nativeBaselineIncompleteError(change, baseline) { + const samplePaths = baseline.omitted.slice(0, 20).map((omission) => omission.path); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + return new NativeBaselineIncompleteError( + change, + baseline.omittedCount, + omittedByReason, + samplePaths, + baseline.omitted.length > samplePaths.length || overflowCount > 0 + ); +} +function nativeBaselineProjectionIncompleteError(change, baseline, projection) { + const retained = new Set(projection.entries.map((entry2) => entry2.path)); + const removedPaths = baseline.entries.filter((entry2) => !retained.has(entry2.path)).slice(0, 20).map((entry2) => entry2.path); + return new NativeBaselineIncompleteError( + change, + projection.omittedCount, + { "manifest-size": projection.omittedCount }, + removedPaths, + projection.omittedCount > removedPaths.length + ); +} +function normalizeProjectRef(value, label) { + const normalized = value.replaceAll("\\", "/").trim(); + if (normalized.length === 0 || normalized !== value.replaceAll("\\", "/") || path45.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(normalized) || normalized.split("/").includes("..") || path45.posix.normalize(normalized) !== normalized || normalized === "." || normalized.endsWith("/") || Buffer.byteLength(normalized, "utf8") > NATIVE_BUILD_EVIDENCE_LIMITS.maxArtifactPathBytes || Array.from(normalized).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + })) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return normalized; +} +function baselineArtifactKind(baseline, artifactRef) { + if (baseline.entries.some((entry2) => entry2.path === artifactRef)) return "file"; + if (baseline.entries.some((entry2) => entry2.path.startsWith(`${artifactRef}/`))) { + return "directory"; + } + return null; +} +async function inspectDeclaredArtifact(paths, baseline, rawRef) { + const artifactRef = normalizeProjectRef(rawRef, "Native build artifact"); + const sensitiveReason = nativeSensitiveArtifactReason(paths, artifactRef); + if (sensitiveReason) { + throw new Error(`Native build artifact is excluded as ${sensitiveReason}: ${artifactRef}`); + } + const target = path45.resolve(paths.projectRoot, ...artifactRef.split("/")); + if (!isInsidePath(paths.projectRoot, target)) { + throw new Error(`Native build artifact escapes the project: ${artifactRef}`); + } + await resolveContainedNativePath(paths.projectRoot, target); + try { + const stat = await fs32.lstat(target); + if (stat.isSymbolicLink()) { + throw new Error(`Native build artifact must not be a symlink or junction: ${artifactRef}`); + } + const realTarget = await fs32.realpath(target); + const realProjectRoot = await fs32.realpath(paths.projectRoot); + if (!isInsidePath(realProjectRoot, realTarget)) { + throw new Error(`Native build artifact resolves outside the project: ${artifactRef}`); + } + if (stat.isFile()) return { path: artifactRef, kind: "file" }; + if (stat.isDirectory()) return { path: artifactRef, kind: "directory" }; + throw new Error(`Native build artifact is not a file or directory: ${artifactRef}`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + const kind = baselineArtifactKind(baseline, artifactRef); + if (kind === null) { + throw new Error(`Native build artifact does not exist: ${artifactRef}`, { cause: error }); + } + return { path: artifactRef, kind }; + } +} +async function collectDeclaredArtifacts(options) { + if (options.refs.length > NATIVE_BUILD_EVIDENCE_LIMITS.maxDeclaredArtifacts) { + throw new Error("Native build evidence exceeds its declared-artifact budget"); + } + const artifacts = await Promise.all( + options.refs.map( + (reference) => inspectDeclaredArtifact(options.paths, options.baseline, reference) + ) + ); + artifacts.sort( + (left, right) => left.path.localeCompare(right.path) || left.kind.localeCompare(right.kind) + ); + if (new Set(artifacts.map((artifact) => artifact.path)).size !== artifacts.length) { + throw new Error("Native build evidence contains duplicate or conflicting artifacts"); + } + return artifacts; +} +function partialFindings(unresolvedScopes) { + return unresolvedScopes.map((scope) => ({ + code: "verification-scope-partial", + message: `${scope.id}: ${scope.reason}`, + ...scope.path === null ? {} : { path: scope.path } + })); +} +function partialScopeHash(value) { + if (!/^[a-f0-9]{64}$/u.test(value)) { + throw new Error("Native partial allowance requires a scope hash"); + } + return value; +} +async function inspectNativeBuildEvidence(options) { + if (options.state.phase !== "build") { + throw new Error(`Native build evidence requires Build, got ${options.state.phase}`); + } + if ((options.noCodeReason ?? "").trim().length > 0 && options.artifactRefs.length > 0) { + throw new Error("Native build evidence cannot combine artifacts with a no-code reason"); + } + const storedBaseline = await readNativeBaselineManifest(options.paths, options.state.name); + if (storedBaseline === null) throw new Error("Native change has no baseline content snapshot"); + const baseline = await filterNativeContentSnapshotToProjectScope(options.paths, storedBaseline); + assertStableNativeSelection(baseline, "baseline projection"); + if (!baseline.complete) { + throw nativeBaselineIncompleteError(options.state.name, baseline); + } + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }); + const declaredArtifacts = await collectDeclaredArtifacts({ + paths: options.paths, + baseline, + refs: options.artifactRefs + }); + const current = await createNativeCurrentContentSnapshot(options.paths, baseline, { + origin: "explicit", + now: options.now + }); + assertStableNativeSelection(current, "current snapshot"); + const bundle = buildNativeImplementationScopeBundle({ + baseline, + current, + contractHash: contract.contract.contractHash, + declaredArtifacts, + noCodeReason: options.noCodeReason ?? null + }); + if (!bundle.baseline.complete) { + throw nativeBaselineProjectionIncompleteError(options.state.name, baseline, bundle.baseline); + } + const scopeRef = nativeEvidenceRef("scopes", bundle.scope.scopeHash); + if (bundle.scope.complete) { + if (options.allowPartialScopeHash !== void 0 && options.allowPartialScopeHash !== null || options.partialReason !== void 0 && options.partialReason !== null) { + throw new Error("Complete Native build evidence must not include a partial allowance"); + } + return { + contract, + bundle, + scopeRef, + allowance: null, + allowanceRef: null, + findings: [], + unresolvedScopes: [] + }; + } + if (options.allowPartialScopeHash === void 0 || options.allowPartialScopeHash === null) { + return { + contract, + bundle, + scopeRef, + allowance: null, + allowanceRef: null, + findings: partialFindings(bundle.scope.unresolvedScopes), + unresolvedScopes: bundle.scope.unresolvedScopes + }; + } + if (!options.confirmed) { + throw new Error("Native partial verification requires explicit confirmation"); + } + const expectedScopeHash = partialScopeHash(options.allowPartialScopeHash); + if (expectedScopeHash !== bundle.scope.scopeHash) { + throw new Error("Native partial allowance does not match the current implementation scope"); + } + const allowance = buildNativePartialAllowance({ + change: options.state.name, + scopeBundle: bundle, + allowedScopeIds: bundle.scope.unresolvedScopes.map((scope) => scope.id), + reason: options.partialReason ?? "", + confirmedSummary: options.confirmedSummary ?? "", + sourceRevision: options.state.revision, + now: options.now + }); + const allowanceRef = nativeEvidenceRef("allowances", allowance.allowanceHash); + return { + contract, + bundle, + scopeRef, + allowance, + allowanceRef, + findings: [], + unresolvedScopes: bundle.scope.unresolvedScopes + }; +} +async function persistNativeBuildEvidence(options) { + const scopeRef = await writeNativeImplementationScope({ + paths: options.paths, + name: options.state.name, + bundle: options.preparation.bundle + }); + if (scopeRef !== options.preparation.scopeRef) { + throw new Error("Native implementation scope persistence ref changed"); + } + if (options.includeAllowance === false || options.preparation.allowance === null) return; + const allowanceRef = await writeNativePartialAllowance({ + paths: options.paths, + name: options.state.name, + allowance: options.preparation.allowance + }); + if (allowanceRef !== options.preparation.allowanceRef) { + throw new Error("Native partial allowance persistence ref changed"); + } +} + +// domains/comet-native/native-transitions.ts +function hasEvidenceRetreatExtras(evidence) { + return evidence.confirmed !== void 0 || evidence.artifacts !== void 0 || evidence.noCodeReason !== void 0 || evidence.allowPartialScopeHash !== void 0 || evidence.partialReason !== void 0 || evidence.verificationResult !== void 0 || evidence.verificationReport !== void 0 || evidence.verificationReceipt !== void 0 || evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0 || evidence.repairOverrideSignature !== void 0 || evidence.repairOverrideSummary !== void 0; +} +function repairFinding(decision) { + if (decision.reasonCode === "override-already-used") { + return { + code: "repair-override-exhausted", + message: `Native repair already used its override for signature: ${decision.signatureHash}` + }; + } + if (decision.disposition === "warn") { + return { + code: "repair-stagnation-warning", + message: `Native repair repeated the same failure signature: ${decision.signatureHash}` + }; + } + if (decision.disposition === "manual-stop") { + return { + code: "repair-stagnation-stop", + message: `Native repair stopped after repeated failure signature: ${decision.signatureHash}` + }; + } + return { + code: "repair-iteration-limit", + message: `Native repair reached its total iteration limit at signature: ${decision.signatureHash}` + }; +} +function validateNativeAdvanceEvidence(evidence) { + assertNativeTrajectoryText(evidence.summary, "Native transition summary"); + if (evidence.noCodeReason !== void 0) { + assertNativeTrajectoryText(evidence.noCodeReason, "Native transition no-code reason"); + } + if (evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0) { + normalizeNativeRepairFailureTokens({ + categories: evidence.repairFailureCategories, + failedCheckIds: evidence.repairFailedCheckIds + }); + } + if (evidence.repairOverrideSignature !== void 0 && !/^[a-f0-9]{64}$/u.test(evidence.repairOverrideSignature)) { + throw new Error("Native repair override signature must be a SHA-256 hash"); + } + if (evidence.repairOverrideSummary !== void 0) { + hashNativeRepairOverrideSummary(evidence.repairOverrideSummary); + } +} +function normalizeNativeAdvanceEvidence(evidence) { + return { + ...evidence, + summary: redactNativeCredentialText(evidence.summary), + ...evidence.noCodeReason === void 0 ? {} : { noCodeReason: redactNativeCredentialText(evidence.noCodeReason) }, + ...evidence.partialReason === void 0 ? {} : { partialReason: redactNativeCredentialText(evidence.partialReason) }, + ...evidence.repairOverrideSummary === void 0 ? {} : { repairOverrideSummary: redactNativeCredentialText(evidence.repairOverrideSummary) } + }; +} +function validateRepairEvidence(state, evidence) { + const hasFailureFacts = evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0; + if (hasFailureFacts && (state.phase !== "verify" || evidence.verificationResult !== "fail")) { + throw new Error("Native repair failure facts are only valid for a failed Verify outcome"); + } + const hasOverrideSignature = evidence.repairOverrideSignature !== void 0; + const hasOverrideSummary = evidence.repairOverrideSummary !== void 0; + if (hasOverrideSignature !== hasOverrideSummary) { + throw new Error("Native repair override signature and summary must be provided together"); + } + if ((hasOverrideSignature || hasOverrideSummary) && state.phase !== "build") { + throw new Error("Native repair override is only valid while leaving Build"); + } +} +async function retreatStaleNativeEvidence(options) { + if (hasEvidenceRetreatExtras(options.transition.evidence)) { + throw new Error("Native evidence retreat only accepts a transition summary"); + } + const previousPhase = options.state.phase; + if (previousPhase !== "verify" && previousPhase !== "archive" || options.run.currentStep !== previousPhase || options.run.pending !== null) { + throw new Error("Native Verify/Archive Run cannot retreat evidence safely"); + } + const evidenceIsFresh = previousPhase === "archive" ? ["complete", "partial"].includes( + (await inspectNativeVerificationFreshness({ + paths: options.transition.paths, + state: options.state, + now: options.transition.now + })).freshness + ) : (await inspectNativeImplementationScopeFreshness({ + paths: options.transition.paths, + state: options.state, + now: options.transition.now + })).freshness === "fresh"; + if (evidenceIsFresh) { + const findings = structureNativeFindings({ + paths: options.transition.paths, + state: options.state, + findings: [ + previousPhase === "archive" ? { + code: "archive-command-required", + message: "Current verification evidence is fresh; use Native Archive preview" + } : { + code: "verification-result-missing", + message: "Current implementation scope is fresh; complete Verify with a result and report" + } + ] + }); + return { + change: options.state, + previousPhase, + next: "manual", + nextCommand: previousPhase === "archive" ? `comet native archive ${options.state.name} --dry-run` : null, + findings, + continuation: nativeContinuation({ + state: options.state, + archiveReady: previousPhase === "archive", + clarificationMode: options.transition.clarificationMode + }) + }; + } + const nextState = { + ...options.state, + revision: options.state.revision + 1, + phase: "build", + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + }; + const nextRun = { + ...options.run, + currentStep: "build", + iteration: options.run.iteration + 1, + pending: null, + status: "running" + }; + const eventData = { + previousPhase, + nextPhase: "build", + evidenceHash: options.evidenceHash, + summary: options.transition.evidence.summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }; + const journal = await prepareNativeTransition({ + paths: options.transition.paths, + previousState: options.state, + nextState, + previousRun: options.run, + nextRun, + evidenceHash: options.evidenceHash, + eventData, + operation: "evidence-retreat", + now: options.transition.now, + transitionId: options.transition.transitionId + }); + await options.transition.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeTransitionLocked( + options.transition.paths, + options.state.name, + options.transition.hooks + ); + if (!persisted) throw new Error("Native evidence retreat journal disappeared before completion"); + return { + change: persisted, + previousPhase, + next: "auto", + nextCommand: null, + findings: [], + continuation: nativeContinuation({ + state: persisted, + clarificationMode: options.transition.clarificationMode + }) + }; +} +async function advanceNativeChange(options) { + const normalizedOptions = { + ...options, + evidence: normalizeNativeAdvanceEvidence(options.evidence) + }; + validateNativeAdvanceEvidence(normalizedOptions.evidence); + return withNativeMutationLock( + options.paths, + `advance ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `advance ${options.name}`, + () => advanceNativeChangeLocked(normalizedOptions) + ) + ); +} +async function advanceNativeChangeLocked(options) { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + const previousPhase = state.phase; + const changeDir = nativeChangeDir(options.paths, options.name); + const hash6 = nativeAdvanceEvidenceHash(options.evidence); + const existingRun = await readNativeRunState(changeDir); + if (existingRun) { + const trajectory = await readNativeTrajectory(changeDir, existingRun.trajectoryRef); + const last = trajectory.at(-1); + if (last?.type === "state_transitioned" && last.data.evidenceHash === hash6 && last.data.nextPhase === state.phase) { + const repair = Object.hasOwn(last.data, "repairStagnation") ? await inspectLatestNativeRepairDecision(options.paths, state) : null; + const repairFindings2 = repair && repair.disposition !== "continue" ? structureNativeFindings({ + paths: options.paths, + state, + findings: [repairFinding(repair)] + }) : []; + const stopped = repair?.disposition === "manual-stop" || repair?.disposition === "hard-stop"; + return { + change: state, + previousPhase: last.data.previousPhase ?? state.phase, + next: stopped ? "manual" : "auto", + nextCommand: stopped ? null : state.phase === "archive" ? `comet native archive ${state.name} --dry-run` : null, + findings: repairFindings2, + continuation: nativeContinuation({ + state, + findings: repairFindings2, + archiveReady: state.phase === "archive" && state.verification_result === "pass", + clarificationMode: options.clarificationMode + }), + ...repair ? { repair } : {} + }; + } + } + if (state.phase === "archive") { + if (!existingRun) throw new Error("Native Archive Run state is missing"); + return retreatStaleNativeEvidence({ + transition: options, + state, + run: existingRun, + evidenceHash: hash6 + }); + } + if (state.phase === "verify" && !hasEvidenceRetreatExtras(options.evidence)) { + if (!existingRun) throw new Error("Native Verify Run state is missing"); + const freshness = await inspectNativeImplementationScopeFreshness({ + paths: options.paths, + state, + now: options.now + }); + if (freshness.freshness !== "fresh") { + return retreatStaleNativeEvidence({ + transition: options, + state, + run: existingRun, + evidenceHash: hash6 + }); + } + } + const candidate = { + ...state, + spec_changes: await reconcileNativeSpecChanges(options.paths, state) + }; + validateRepairEvidence(state, options.evidence); + const guard = await inspectNativeGuard({ + paths: options.paths, + state: candidate, + evidence: options.evidence, + clarificationMode: options.clarificationMode + }); + if (!guard.valid) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: guard.findings + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + const shapeContract = state.phase === "shape" ? await collectNativeContractFiles({ + changeDir, + briefRef: candidate.brief, + specChanges: candidate.spec_changes + }) : null; + if (state.phase !== "build" && (options.evidence.allowPartialScopeHash !== void 0 || options.evidence.partialReason !== void 0)) { + throw new Error("Native partial scope allowance is only valid while leaving Build"); + } + const buildEvidence = state.phase === "build" ? await inspectNativeBuildEvidence({ + paths: options.paths, + state: candidate, + artifactRefs: options.evidence.artifacts ?? [], + noCodeReason: options.evidence.noCodeReason ?? null, + allowPartialScopeHash: options.evidence.allowPartialScopeHash ?? null, + partialReason: options.evidence.partialReason ?? null, + confirmedSummary: options.evidence.summary, + confirmed: options.evidence.confirmed ?? false, + now: options.now + }) : null; + if (buildEvidence && (state.approved_contract_hash ?? null) !== buildEvidence.contract.contract.contractHash && !options.evidence.confirmed) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: [ + { + code: "contract-changed-after-approval", + message: "Native contract changed after approval; re-confirm the current contract before leaving Build" + } + ] + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + const preparedScope = buildEvidence ? { + scopeHash: buildEvidence.bundle.scope.scopeHash, + scopeRef: buildEvidence.scopeRef, + complete: buildEvidence.bundle.scope.complete, + unresolvedScopeCount: buildEvidence.unresolvedScopes.length, + partialAllowanceRef: buildEvidence.allowanceRef, + acceptancePage: projectNativeAcceptancePage({ + criteria: buildEvidence.contract.contract.acceptance, + acceptanceHash: buildEvidence.contract.contract.acceptanceHash + }) + } : void 0; + if (buildEvidence && buildEvidence.findings.length > 0) { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence, + includeAllowance: false + }); + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: buildEvidence.findings + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }), + preparedScope + }; + } + let repairEventProjection = null; + let repairScopeHashForEvent = buildEvidence ? nativeRepairScopeHash(buildEvidence.bundle) : null; + if (state.phase === "build" && buildEvidence) { + const repairGuard = await inspectNativeRepairBuildGuard({ + paths: options.paths, + state, + currentImplementationScope: buildEvidence.bundle, + ...options.evidence.repairOverrideSignature && options.evidence.repairOverrideSummary ? { + override: { + expectedSignatureHash: options.evidence.repairOverrideSignature, + summary: options.evidence.repairOverrideSummary + } + } : {} + }); + if (repairGuard.disposition !== "proceed") { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence, + includeAllowance: false + }); + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: [ + repairGuard.disposition === "hard-stop" && repairGuard.overrideRecorded ? { + code: "repair-override-exhausted", + message: `Native repair already used its override for signature: ${repairGuard.signatureHash}` + } : repairFinding({ + disposition: repairGuard.disposition, + reasonCode: repairGuard.disposition === "hard-stop" ? "repair-iteration-limit" : "repeated-failure-stop", + signatureHash: repairGuard.signatureHash + }) + ] + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }), + preparedScope: preparedScope ? { ...preparedScope, partialAllowanceRef: null } : preparedScope + }; + } + repairEventProjection = repairGuard.eventProjection; + } + const verificationEvidence = state.phase === "verify" ? await inspectNativeVerificationEvidence({ + paths: options.paths, + state: candidate, + result: options.evidence.verificationResult, + reportRef: options.evidence.verificationReport, + receiptRef: options.evidence.verificationReceipt ?? null, + now: options.now + }) : null; + if (verificationEvidence && !verificationEvidence.ready) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: verificationEvidence.findingCodes.map((code) => ({ + code, + message: `Native verification evidence is not current: ${code}` + })) + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + let repairDecision = null; + if (state.phase === "verify" && options.evidence.verificationResult === "fail" && verificationEvidence?.ready && verificationEvidence.envelope) { + const repairResult = await inspectNativeRepairFailureForTransition({ + paths: options.paths, + state, + envelope: verificationEvidence.envelope, + ...options.evidence.repairFailureCategories ? { categories: options.evidence.repairFailureCategories } : {}, + ...options.evidence.repairFailedCheckIds ? { failedCheckIds: options.evidence.repairFailedCheckIds } : {} + }); + repairEventProjection = repairResult.eventProjection; + repairScopeHashForEvent = repairResult.facts.implementationScopeHash; + repairDecision = projectNativeRepairDecision(repairResult); + } + let run = existingRun; + if (!run) { + if (state.run_id !== null || state.phase !== "shape") { + throw new Error("Native Run state is missing or inconsistent"); + } + run = startNativeRun( + NATIVE_RUNTIME_PACKAGE, + options.runId?.() ?? randomUUID11(), + NATIVE_RUNTIME_HASH + ); + } + if (run.currentStep !== state.phase) { + throw new Error(`Native Run step ${run.currentStep ?? "(none)"} does not match ${state.phase}`); + } + const decision = decideWithResolver( + NATIVE_RUNTIME_PACKAGE, + run, + /* @__PURE__ */ new Set(), + nativePhaseResolver, + void 0 + ); + if (!decision.action) throw new Error(decision.reason ?? "Native runtime produced no action"); + const advanced = recordOutcomeWithResolver( + NATIVE_RUNTIME_PACKAGE, + decision.state, + { + actionId: decision.action.id, + status: "succeeded", + summary: options.evidence.summary, + state: options.evidence.verificationResult ? { verification_result: options.evidence.verificationResult } : void 0 + }, + nativePhaseResolver, + void 0 + ); + if (!advanced.currentStep) throw new Error("Archive completion must use the archive command"); + const updated = { + ...candidate, + revision: state.revision + 1, + phase: advanced.currentStep, + approval: options.evidence.confirmed ? "confirmed" : state.approval, + approved_contract_hash: state.phase === "shape" ? shapeContract.contract.contractHash : state.phase === "build" && options.evidence.confirmed ? buildEvidence.contract.contract.contractHash : state.approved_contract_hash ?? null, + run_id: run.runId, + ...state.phase === "build" ? { + verification_result: "pending", + verification_report: null, + implementation_scope: buildEvidence.scopeRef, + partial_allowance: buildEvidence.allowanceRef, + verification_evidence: null + } : {}, + ...state.phase === "verify" ? { + verification_result: options.evidence.verificationResult, + verification_report: verificationEvidence.envelope.reportRef, + verification_evidence: verificationEvidence.evidenceRef + } : {} + }; + const eventData = { + previousPhase, + nextPhase: updated.phase, + evidenceHash: hash6, + summary: options.evidence.summary, + artifacts: options.evidence.artifacts ?? [], + noCodeReason: options.evidence.noCodeReason ?? null, + verificationResult: options.evidence.verificationResult ?? null, + ...(state.phase === "build" || state.phase === "verify") && (state.phase === "build" ? buildEvidence.bundle.scope.scopeHash : verificationEvidence.envelope.implementationScopeHash) ? { + implementationScopeHash: state.phase === "build" ? buildEvidence.bundle.scope.scopeHash : verificationEvidence.envelope.implementationScopeHash + } : {}, + ...repairScopeHashForEvent ? { repairScopeHash: repairScopeHashForEvent } : {}, + ...repairEventProjection ? { repairStagnation: repairEventProjection } : {} + }; + if (state.phase === "build" && buildEvidence) { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence + }); + } + if (state.phase === "verify" && verificationEvidence) { + await persistNativeVerificationEvidence({ + paths: options.paths, + state, + preparation: verificationEvidence + }); + } + const journal = await prepareNativeTransition({ + paths: options.paths, + previousState: state, + nextState: updated, + previousRun: existingRun, + nextRun: advanced, + evidenceHash: hash6, + eventData, + now: options.now, + transitionId: options.transitionId + }); + await options.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeTransitionLocked( + options.paths, + options.name, + options.hooks + ); + if (!persisted) throw new Error("Native transition journal disappeared before completion"); + const repairFindings = repairDecision && repairDecision.disposition !== "continue" ? structureNativeFindings({ + paths: options.paths, + state: persisted, + findings: [repairFinding(repairDecision)] + }) : []; + const repairStopped = repairDecision?.disposition === "manual-stop" || repairDecision?.disposition === "hard-stop"; + return { + change: persisted, + previousPhase, + next: repairStopped ? "manual" : "auto", + nextCommand: repairStopped ? null : persisted.phase === "archive" ? `comet native archive ${persisted.name} --dry-run` : null, + findings: repairFindings, + continuation: nativeContinuation({ + state: persisted, + findings: repairFindings, + archiveReady: persisted.phase === "archive" && persisted.verification_result === "pass", + clarificationMode: options.clarificationMode + }), + ...preparedScope ? { preparedScope } : {}, + ...repairDecision ? { repair: repairDecision } : {} + }; +} + +// domains/comet-native/native-hook-guard.ts +import { promises as fs33 } from "fs"; +import path46 from "path"; + +// domains/comet-entry/hook-adapter.ts +import { readFileSync } from "fs"; +var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "applypatch", + "create", + "createfile", + "deletefile", + "edit", + "editfile", + "patch", + "strreplaceeditor", + "write", + "writefile", + "writefiletool" +]); +var NON_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "glob", + "grep", + "listfiles", + "read", + "readfile", + "search", + "view" +]); +var SINGULAR_PATH_KEYS = ["file_path", "filePath", "path", "target_file", "targetFile"]; +var PLURAL_PATH_KEYS = ["file_paths", "filePaths", "paths", "files", "targets"]; +var NESTED_TARGET_KEYS = ["operations", "edits"]; +var PATCH_KEYS = ["patch", "diff", "patchText", "patch_text", "changes"]; +function isRecord2(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function normalizedToolName(value) { + return value.toLowerCase().replace(/[^a-z0-9]+/gu, ""); +} +function readToolName(input) { + for (const key of ["tool_name", "toolName", "tool", "name"]) { + const value = input[key]; + if (typeof value === "string" && value.trim()) return value.trim(); + } + return null; +} +function parseJsonValue(value) { + if (typeof value !== "string") return value; + const source = value.trim(); + if (!source.startsWith("{") && !source.startsWith("[")) return value; + try { + return JSON.parse(source); + } catch { + return value; + } +} +function readToolArguments(input) { + for (const key of ["tool_input", "toolInput", "toolArgs", "tool_args", "arguments"]) { + if (input[key] !== void 0) return parseJsonValue(input[key]); + } + return input; +} +function patchTargets(source) { + const targets = []; + const patterns = [ + /^\*\*\* (?:Add|Update|Delete) File:\s+(.+?)\s*$/gmu, + /^\+\+\+\s+(?:b\/)?(.+?)\s*$/gmu + ]; + for (const pattern of patterns) { + for (const match of source.matchAll(pattern)) { + const target = match[1]?.trim(); + if (target && target !== "/dev/null") targets.push(target); + } + } + return targets; +} +function addTarget(targets, value) { + if (typeof value === "string") { + const target = value.trim(); + if (target) targets.push(target); + return; + } + if (Array.isArray(value)) { + for (const entry2 of value) addTarget(targets, entry2); + return; + } + if (!isRecord2(value)) return; + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, value[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, value[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, value[key]); +} +function collectTargets(input, args) { + const targets = []; + const records = [args, input].filter(isRecord2); + for (const record8 of records) { + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, record8[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, record8[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, record8[key]); + for (const key of PATCH_KEYS) { + const value = record8[key]; + if (typeof value === "string") targets.push(...patchTargets(value)); + } + } + if (typeof args === "string") targets.push(...patchTargets(args)); + return [...new Set(targets)]; +} +function parseCometHookRequest(source, filePath) { + if (filePath?.trim()) { + return { intent: "write", targets: [filePath.trim()], toolName: null }; + } + if (!source.trim()) return { intent: "unknown", targets: [], toolName: null }; + let input; + try { + input = JSON.parse(source); + } catch { + return { intent: "unknown", targets: [], toolName: null }; + } + if (!isRecord2(input)) return { intent: "unknown", targets: [], toolName: null }; + const toolName = readToolName(input); + const targets = collectTargets(input, readToolArguments(input)); + if (toolName && WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName + }; + } + if (toolName && NON_WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { intent: "non-write", targets: [], toolName }; + } + if (toolName) return { intent: "unknown", targets, toolName }; + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName: null + }; +} +function readCometHookRequest() { + const filePath = process.env.FILE_PATH; + if (filePath?.trim()) return parseCometHookRequest("", filePath); + if (process.stdin.isTTY) return parseCometHookRequest("", filePath); + try { + return parseCometHookRequest(readFileSync(0, "utf8"), filePath); + } catch { + return parseCometHookRequest("", filePath); + } +} + +// domains/comet-native/native-hook-guard.ts +function isWithin(parent, target) { + const relative = path46.relative(parent, target); + return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative); +} +function requestTargetsAreControlOnly(projectRoot, nativeRoot, request) { + return request.targets.length > 0 && request.targets.every((targetPath) => { + const target = path46.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) return true; + const relative = path46.relative(projectRoot, target).replaceAll("\\", "/"); + return relative === ".comet/config.yaml" || isWithin(nativeRoot, target); + }); +} +async function activeNativeContext(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (!config || !(config.workflows ?? [config.default_workflow]).includes("native")) return null; + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + let entries; + try { + entries = await fs33.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return { paths, changes: [] }; + throw error; + } + const changes = []; + for (const entry2 of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + const state = await readNativeChange(paths, entry2.name); + if (!state.archived) changes.push(state); + } + return { paths, changes }; +} +async function readNativeHookRequest() { + const { intent, targets } = await readCometHookRequest(); + return { intent, targets }; +} +async function inspectNativeHookGuard(projectRoot, request, selectedChangeName) { + const context = await activeNativeContext(projectRoot); + if (!context) return { allowed: true, reason: "Native workflow is not enabled" }; + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (context.changes.length === 0) { + return { + allowed: true, + reason: requestTargetsAreControlOnly(projectRoot, context.paths.nativeRoot, request) ? "Native control artifact write" : "No Native changes exist" + }; + } + let change; + if (selectedChangeName) { + change = context.changes.find((candidate) => candidate.name === selectedChangeName); + if (!change) { + return { + allowed: false, + reason: `Selected Native change ${selectedChangeName} is missing or archived; resume /comet-native before retrying`, + workflow: "native", + change: selectedChangeName + }; + } + } else if (context.changes.length === 1) { + change = context.changes[0]; + } else { + const selectedName2 = await resolveSelectedNativeChange(context.paths); + change = context.changes.find((candidate) => candidate.name === selectedName2); + if (!change) { + return { + allowed: false, + reason: "Multiple Native changes are active; select the change to resume before writing code", + workflow: "native" + }; + } + } + if (change.phase === "build") { + return { + allowed: true, + reason: "Native change is in Build", + workflow: "native", + phase: change.phase, + change: change.name + }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + return { + allowed: false, + reason: `Hook write target could not be determined while Native change ${change.name} is in ${change.phase}; resume /comet-native before retrying`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + let controlTarget = false; + let externalTarget = false; + for (const targetPath of request.targets) { + const target = path46.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) { + externalTarget = true; + continue; + } + const relative = path46.relative(projectRoot, target).replaceAll("\\", "/"); + if (relative === ".comet/config.yaml" || isWithin(context.paths.nativeRoot, target)) { + controlTarget = true; + continue; + } + return { + allowed: false, + reason: `Native change ${change.name} is in ${change.phase}; implementation writes are only allowed in build. Resume /comet-native to continue safely`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + return { + allowed: true, + reason: controlTarget ? "Native control artifact write" : externalTarget ? "Write target is outside the guarded project" : "No guarded write target was provided", + workflow: "native", + phase: change.phase, + change: change.name + }; +} + +// domains/comet-native/native-cli.ts +var NATIVE_SHOW_MAX_SERIALIZED_BYTES = 10 * 1024 * 1024; +var NativeUsageError = class extends Error { +}; +var USAGE = `Usage: comet native [options] + +Commands: + hook-guard [--hook-output copilot] + init [--root ] [--language en|zh-CN] + root show + root move + new [--language en|zh-CN] + spec remove + spec rebase --summary + list [--cursor ] + show + status [] [--cursor ] [--details [--acceptance-cursor ]] + select + checkpoint --summary --next-action [--artifact ] [--expect-revision ] + check + evidence format [--entries ] + next --summary [--confirmed] [--artifact ] [--no-code-reason ] [--allow-partial-scope --partial-reason ] [--result pass|fail] [--report ] [--receipt ] [--failure-category ] [--failed-check ] [--override-repair --override-summary ] + archive --dry-run + archive --expect-preflight + doctor [] [--repair] [--strategy continue|rollback] +`; +function takeFlag(args, name) { + const indexes = args.flatMap((value, index) => value === name ? [index] : []); + if (indexes.length > 1) throw new NativeUsageError(`${name} may only be provided once`); + if (indexes.length === 0) return false; + args.splice(indexes[0], 1); + return true; +} +function takeOption(args, name) { + const indexes = args.flatMap((value2, index2) => value2 === name ? [index2] : []); + if (indexes.length > 1) throw new NativeUsageError(`${name} may only be provided once`); + if (indexes.length === 0) return void 0; + const index = indexes[0]; + const value = args[index + 1]; + if (value === void 0 || value.startsWith("--")) { + throw new NativeUsageError(`${name} requires a value`); + } + args.splice(index, 2); + return value; +} +function takeMany(args, name) { + const values = []; + for (let index = 0; index < args.length; ) { + if (args[index] !== name) { + index += 1; + continue; + } + const value = args[index + 1]; + if (value === void 0 || value.startsWith("--")) { + throw new NativeUsageError(`${name} requires a value`); + } + values.push(value); + args.splice(index, 2); + } + return values; +} +function assertNoArguments(args) { + if (args.length > 0) throw new NativeUsageError(`Unexpected argument: ${args[0]}`); +} +function requiredPositional(args, label) { + const value = args.shift(); + if (!value || value.startsWith("--")) throw new NativeUsageError(`${label} is required`); + return value; +} +function languageOption(args, fallback = "en") { + const language = takeOption(args, "--language") ?? fallback; + if (language !== "en" && language !== "zh-CN") { + throw new NativeUsageError("--language must be en or zh-CN"); + } + return language; +} +function revisionOption(args) { + const value = takeOption(args, "--expect-revision"); + if (value === void 0) return void 0; + if (!/^[1-9]\d*$/u.test(value) || !Number.isSafeInteger(Number(value))) { + throw new NativeUsageError("--expect-revision must be a positive integer"); + } + return Number(value); +} +async function projectRootFrom(explicit) { + return explicit ? path47.resolve(explicit) : discoverNativeProject(process.cwd()); +} +async function configuredPaths(projectRoot) { + const resolved = await resolveNativeProject({ + startPath: projectRoot, + allowMissingConfig: false + }); + return { config: resolved.config, paths: resolved.paths }; +} +async function doctorPaths(projectRoot) { + const config = await readProjectConfig(projectRoot); + return nativeProjectPaths(projectRoot, config?.native.artifact_root ?? "docs"); +} +function success(command, data, text) { + return { command, exitCode: 0, data, text: text ?? JSON.stringify(data, null, 2) + "\n" }; +} +async function readBoundedEvidenceFile(filePath, maxBytes) { + try { + const { bytes } = await readFileRaceSafe(filePath, maxBytes, { + label: "Acceptance evidence entries file" + }); + return bytes.toString("utf8"); + } catch (error) { + if (error instanceof RaceSafeReadError) { + if (error.reason === "not-regular-file") { + throw new Error(`Acceptance evidence entries path is not a regular file: ${filePath}`, { + cause: error + }); + } + if (error.reason === "too-large") { + throw new Error(`Acceptance evidence entries file exceeds ${maxBytes} bytes: ${filePath}`, { + cause: error + }); + } + throw new Error(`Acceptance evidence entries file changed while reading: ${filePath}`, { + cause: error + }); + } + throw error; + } +} +async function readBoundedEvidenceStdin(maxBytes) { + const chunks = []; + let total = 0; + for await (const chunk of process.stdin) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)); + total += buffer.byteLength; + if (total > maxBytes) { + throw new Error(`Acceptance evidence entries on stdin exceed ${maxBytes} bytes`); + } + chunks.push(buffer); + } + return Buffer.concat(chunks).toString("utf8"); +} +async function dispatch(rawArgs, explicitProjectRoot) { + if (rawArgs.length === 0 || rawArgs[0] === "--help" || rawArgs[0] === "help") { + return { command: rawArgs[0] ?? null, exitCode: 0, data: { usage: USAGE }, text: USAGE }; + } + const command = rawArgs.shift(); + const projectRoot = await projectRootFrom(explicitProjectRoot); + if (command === "hook-guard") { + const hookOutput = takeOption(rawArgs, "--hook-output"); + if (hookOutput !== void 0 && hookOutput !== "copilot") { + throw new NativeUsageError("--hook-output must be copilot"); + } + assertNoArguments(rawArgs); + const result2 = await inspectNativeHookGuard(projectRoot, await readNativeHookRequest()); + if (hookOutput === "copilot") { + return { + command, + exitCode: 0, + data: result2, + text: result2.allowed ? "{}\n" : `${JSON.stringify({ + permissionDecision: "deny", + permissionDecisionReason: result2.reason + })} +` + }; + } + return result2.allowed ? { command, exitCode: 0, data: result2 } : { + command, + exitCode: 2, + data: result2, + error: { code: "blocked", message: result2.reason } + }; + } + if (command === "init") { + const requestedRoot = takeOption(rawArgs, "--root"); + const existing = await readProjectConfig(projectRoot); + const language = languageOption(rawArgs, existing?.native.language ?? "en"); + assertNoArguments(rawArgs); + if (existing?.native.pending_root_move) { + throw new Error(`Native root move ${existing.native.pending_root_move.id} is incomplete`); + } + const artifactRoot = normalizeArtifactRootRef( + requestedRoot ?? existing?.native.artifact_root ?? "docs" + ); + if (existing && requestedRoot && existing.native.artifact_root !== artifactRoot) { + throw new Error( + `Configured Native artifact root is ${existing.native.artifact_root}; refusing conflicting root ${artifactRoot}` + ); + } + const config = existing ? { ...existing, native: { ...existing.native, language } } : defaultProjectConfig(artifactRoot, language); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + await ensureNativeDirectories(paths); + await writeProjectConfig(projectRoot, config); + return success( + "init", + { + projectRoot, + artifactRoot: config.native.artifact_root, + nativeRoot: paths.nativeRoot, + language + }, + `Initialized Comet Native at ${paths.nativeRoot} +` + ); + } + if (command === "root") { + const subcommand = requiredPositional(rawArgs, "root subcommand"); + if (subcommand === "show") { + assertNoArguments(rawArgs); + const config = await readProjectConfig(projectRoot); + if (!config) throw new Error(".comet/config.yaml was not found"); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + return success("root show", { + projectRoot, + artifactRoot: config.native.artifact_root, + language: config.native.language, + nativeRoot: paths.nativeRoot, + pendingRootMove: config.native.pending_root_move ?? null + }); + } + if (subcommand === "move") { + const target = requiredPositional(rawArgs, "artifact root"); + assertNoArguments(rawArgs); + const result2 = await moveNativeRoot({ projectRoot, toArtifactRoot: target }); + return success("root move", result2, `Moved Comet Native to ${result2.toNativeRoot} +`); + } + throw new NativeUsageError(`Unknown root command: ${subcommand}`); + } + if (command === "new") { + const name = requiredPositional(rawArgs, "change name"); + let config = await readProjectConfig(projectRoot); + const language = languageOption(rawArgs, config?.native.language ?? "en"); + assertNoArguments(rawArgs); + const shouldWriteConfig = config === null; + if (!config) { + config = defaultProjectConfig("docs", language); + } + if (config.native.pending_root_move) { + throw new Error(`Native root move ${config.native.pending_root_move.id} is incomplete`); + } + if (shouldWriteConfig) await writeProjectConfig(projectRoot, config); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + await ensureNativeDirectories(paths); + const state = await createNativeChange({ paths, name, language }); + await selectNativeChange(paths, state.name); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "new", + { ...state, continuation: status.continuation }, + `Created Native change ${state.name} +` + ); + } + if (command === "spec") { + const subcommand = requiredPositional(rawArgs, "spec subcommand"); + if (subcommand === "remove") { + const name = requiredPositional(rawArgs, "change name"); + const capability = requiredPositional(rawArgs, "capability"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const state = await markNativeSpecRemoval(paths, name, capability); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "spec remove", + { ...state, continuation: status.continuation }, + `Marked Native capability ${capability} for removal in ${name} +` + ); + } + if (subcommand === "rebase") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const state = await rebaseNativeSpecChanges({ paths, name, summary }); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "spec rebase", + { ...state, continuation: status.continuation }, + `Rebased Native specs for ${name} +` + ); + } + throw new NativeUsageError(`Unknown spec command: ${subcommand}`); + } + if (command === "list") { + const cursor = takeOption(rawArgs, "--cursor"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const page = await listNativeStatusPage(paths, { + ...cursor ? { cursor } : {}, + clarificationMode: config.native.clarification_mode + }); + return success("list", page); + } + if (command === "show") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + return success("show", { + name, + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + migrationRequired: true, + message: inspection.message + }); + } + if (inspection.status !== "current" || !inspection.state) { + throw new NativeRuntimeCompatibilityError( + inspection.schema, + inspection.minimumRuntimeVersion + ); + } + const state = inspection.state; + const changeDir = nativeChangeDir(paths, name); + const proposedSpecs = await readNativeProposedSpecs(paths, name); + const brief = await readNativeBoundedTextFile({ + root: changeDir, + ref: state.brief, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + const payload = { + state, + brief: brief.text, + proposedSpecs + }; + if (Buffer.byteLength(JSON.stringify(payload), "utf8") > NATIVE_SHOW_MAX_SERIALIZED_BYTES) { + throw new Error("Native show output exceeds its serialized byte budget"); + } + return success("show", payload); + } + if (command === "status") { + const details = takeFlag(rawArgs, "--details"); + const cursor = takeOption(rawArgs, "--cursor"); + const acceptanceCursor2 = takeOption(rawArgs, "--acceptance-cursor"); + const name = rawArgs[0]?.startsWith("--") ? void 0 : rawArgs.shift(); + if (details && !name) throw new NativeUsageError("status --details requires a change name"); + if (cursor && name) throw new NativeUsageError("--cursor is only valid for status lists"); + if (cursor && details) throw new NativeUsageError("--cursor cannot be combined with --details"); + if (acceptanceCursor2 && !details) { + throw new NativeUsageError("--acceptance-cursor requires status --details"); + } + if (acceptanceCursor2 && !name) { + throw new NativeUsageError("--acceptance-cursor requires a change name"); + } + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const data = name ? await inspectNativeStatus(paths, name, { + details, + ...acceptanceCursor2 ? { acceptanceCursor: acceptanceCursor2 } : {}, + clarificationMode: config.native.clarification_mode + }) : await listNativeStatusPage(paths, { + ...cursor ? { cursor } : {}, + clarificationMode: config.native.clarification_mode + }); + return success("status", data); + } + if (command === "select") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + await selectNativeChange(paths, name); + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "select", + { selected: name, continuation: status.continuation }, + `Selected Native change ${name} +` + ); + } + if (command === "checkpoint") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + const nextAction = takeOption(rawArgs, "--next-action"); + if (!nextAction) throw new NativeUsageError("--next-action is required"); + const artifacts = takeMany(rawArgs, "--artifact"); + const expectedRevision = revisionOption(rawArgs); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const result2 = await checkpointNativeChange({ + paths, + name, + summary, + nextAction, + artifacts, + expectedRevision + }); + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + const manifestRef = path47.relative( + paths.projectRoot, + path47.join(nativeChangeDir(paths, name), ...result2.checkpoint.manifestRef.split("/")) + ).replaceAll("\\", "/"); + return success("checkpoint", { + ...result2, + checkpoint: { ...result2.checkpoint, manifestRef }, + continuation: status.continuation + }); + } + if (command === "check") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + const checked = await checkNativeChange({ paths, name }); + const data = { + ref: checked.ref, + hash: checked.receipt.receiptHash, + status: checked.receipt.status, + checker: checked.receipt.checker, + counts: checked.receipt.counts, + issues: checked.receipt.issues, + issuesTruncated: checked.receipt.issuesTruncated, + stale: checked.receipt.stale, + staleReasons: checked.receipt.staleReasons, + startedAt: checked.receipt.startedAt, + endedAt: checked.receipt.endedAt, + sourceRevision: checked.receipt.sourceRevision + }; + const passed = checked.receipt.status === "passed" && !checked.receipt.stale; + return { + command: "check", + exitCode: passed ? 0 : 1, + data, + text: `Native check ${passed ? "passed" : "failed"}: ${checked.ref} +` + }; + } + if (command === "evidence") { + const subcommand = requiredPositional(rawArgs, "evidence subcommand"); + if (subcommand === "format") { + const entriesPath = takeOption(rawArgs, "--entries"); + assertNoArguments(rawArgs); + let raw; + if (entriesPath) { + raw = await readBoundedEvidenceFile( + path47.resolve(entriesPath), + MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES + ); + } else { + if (process.stdin.isTTY) { + throw new NativeUsageError( + "evidence format requires acceptance evidence entries JSON on stdin, or --entries " + ); + } + raw = await readBoundedEvidenceStdin(MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES); + } + let entries; + try { + entries = JSON.parse(raw); + } catch (error) { + throw new Error( + `Acceptance evidence entries must be valid JSON: ${error.message}`, + { cause: error } + ); + } + if (!Array.isArray(entries)) { + throw new Error("Acceptance evidence entries must be a JSON array"); + } + const block = serializeNativeVerificationMachineBlock(entries); + return success("evidence format", { block }, `${block} +`); + } + throw new NativeUsageError(`Unknown evidence command: ${subcommand}`); + } + if (command === "next") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + const confirmed = takeFlag(rawArgs, "--confirmed"); + const artifacts = takeMany(rawArgs, "--artifact"); + const noCodeReason = takeOption(rawArgs, "--no-code-reason"); + const allowPartialScopeHash = takeOption(rawArgs, "--allow-partial-scope"); + const partialReason = takeOption(rawArgs, "--partial-reason"); + const verificationResult = takeOption(rawArgs, "--result"); + const verificationReport = takeOption(rawArgs, "--report"); + const verificationReceipt = takeOption(rawArgs, "--receipt"); + const repairFailureCategories = takeMany(rawArgs, "--failure-category"); + const repairFailedCheckIds = takeMany(rawArgs, "--failed-check"); + const repairOverrideSignature = takeOption(rawArgs, "--override-repair"); + const repairOverrideSummary = takeOption(rawArgs, "--override-summary"); + if (verificationResult !== void 0 && verificationResult !== "pass" && verificationResult !== "fail") { + throw new NativeUsageError("--result must be pass or fail"); + } + if (allowPartialScopeHash === void 0 !== (partialReason === void 0)) { + throw new NativeUsageError( + "--allow-partial-scope and --partial-reason must be provided together" + ); + } + if (allowPartialScopeHash && !/^[a-f0-9]{64}$/u.test(allowPartialScopeHash)) { + throw new NativeUsageError("--allow-partial-scope must be a SHA-256 hash"); + } + if (allowPartialScopeHash && !confirmed) { + throw new NativeUsageError("--allow-partial-scope requires --confirmed"); + } + if ((repairFailureCategories.length > 0 || repairFailedCheckIds.length > 0) && verificationResult !== "fail") { + throw new NativeUsageError("--failure-category and --failed-check require --result fail"); + } + if (verificationReceipt && verificationResult === void 0) { + throw new NativeUsageError("--receipt requires --result"); + } + if (repairOverrideSignature === void 0 !== (repairOverrideSummary === void 0)) { + throw new NativeUsageError( + "--override-repair and --override-summary must be provided together" + ); + } + if (repairOverrideSignature && !/^[a-f0-9]{64}$/u.test(repairOverrideSignature)) { + throw new NativeUsageError("--override-repair must be a SHA-256 hash"); + } + if (repairOverrideSignature && verificationResult !== void 0) { + throw new NativeUsageError("--override-repair cannot be combined with --result"); + } + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const evidence = { + summary, + ...confirmed ? { confirmed: true } : {}, + ...artifacts.length > 0 ? { artifacts } : {}, + ...noCodeReason ? { noCodeReason } : {}, + ...allowPartialScopeHash ? { allowPartialScopeHash } : {}, + ...partialReason ? { partialReason } : {}, + ...verificationResult ? { verificationResult } : {}, + ...verificationReport ? { verificationReport } : {}, + ...verificationReceipt ? { verificationReceipt } : {}, + ...repairFailureCategories.length > 0 ? { repairFailureCategories } : {}, + ...repairFailedCheckIds.length > 0 ? { repairFailedCheckIds } : {}, + ...repairOverrideSignature ? { repairOverrideSignature } : {}, + ...repairOverrideSummary ? { repairOverrideSummary } : {} + }; + const result2 = await advanceNativeChange({ + paths, + name, + evidence, + clarificationMode: config.native.clarification_mode + }); + if (result2.next === "manual") { + const repairBlocked = result2.repair?.disposition === "manual-stop" || result2.repair?.disposition === "hard-stop" || result2.findings.some( + (finding) => [ + "repair-stagnation-stop", + "repair-iteration-limit", + "repair-override-exhausted" + ].includes(finding.code) + ); + return { + command: "next", + exitCode: repairBlocked ? 75 : 65, + data: result2, + error: { + code: repairBlocked ? "blocked" : "invalid-data", + message: result2.findings[0]?.message ?? "Native phase guard failed" + } + }; + } + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + return success("next", { ...result2, continuation: status.continuation }); + } + if (command === "archive") { + const name = requiredPositional(rawArgs, "change name"); + const dryRun = takeFlag(rawArgs, "--dry-run"); + const expectedPreflightHash = takeOption(rawArgs, "--expect-preflight"); + if (dryRun && expectedPreflightHash) { + throw new NativeUsageError("--dry-run and --expect-preflight cannot be combined"); + } + if (!dryRun && !expectedPreflightHash) { + throw new NativeUsageError("archive requires --dry-run or --expect-preflight "); + } + if (expectedPreflightHash && !/^[a-f0-9]{64}$/u.test(expectedPreflightHash)) { + throw new NativeUsageError("--expect-preflight must be a SHA-256 hash"); + } + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + if (dryRun) { + const preview = await inspectNativeArchivePreflight({ paths, name }); + return success( + "archive --dry-run", + preview, + `Native Archive preview ${preview.preflightHash}: ${preview.ready ? "ready" : "blocked"} +` + ); + } + const state = await readNativeChange(paths, name); + const result2 = await archiveNativeChange({ + paths, + name, + expectedPreflightHash + }); + return success( + "archive", + { ...result2, continuation: nativeContinuation({ state, done: true }) }, + `Archived Native change ${name} to ${result2.archiveDir} +` + ); + } + if (command === "doctor") { + const repair = takeFlag(rawArgs, "--repair"); + const recoveryStrategy = takeOption(rawArgs, "--strategy"); + if (recoveryStrategy !== void 0 && recoveryStrategy !== "continue" && recoveryStrategy !== "rollback") { + throw new NativeUsageError("--strategy must be continue or rollback"); + } + const name = rawArgs[0]?.startsWith("--") ? void 0 : rawArgs.shift(); + assertNoArguments(rawArgs); + const paths = await doctorPaths(projectRoot); + const result2 = await doctorNativeProject({ + paths, + ...name ? { name } : {}, + repair, + ...recoveryStrategy ? { recoveryStrategy } : {} + }); + return result2.healthy ? success("doctor", result2) : { + command: "doctor", + exitCode: 65, + data: result2, + error: { code: "invalid-data", message: "Native project needs attention" } + }; + } + throw new NativeUsageError(`Unknown Native command: ${command}`); +} +function errorResult(command, error) { + if (error instanceof NativeUsageError) { + return { + command, + exitCode: 64, + error: { code: "usage", message: error.message } + }; + } + if (error instanceof NativeSpecConflictError) { + return { + command, + exitCode: 73, + data: { + capability: error.capability, + expectedHash: error.expectedHash, + actualHash: error.actualHash, + canonicalPath: error.canonicalPath + }, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeArchivePreflightError) { + return { + command, + exitCode: 73, + data: error.preflight, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeChangeRevisionConflictError) { + return { + command, + exitCode: 73, + data: { + change: error.change, + expectedRevision: error.expectedRevision, + actualRevision: error.actualRevision, + outcome: "revision-conflict" + }, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeBaselineIncompleteError) { + return { + command, + exitCode: 65, + data: { + change: error.change, + complete: false, + omittedCount: error.omittedCount, + omittedByReason: error.omittedByReason, + samplePaths: error.samplePaths, + sampleTruncated: error.sampleTruncated, + effectiveLimits: error.effectiveLimits, + policyHash: error.policyHash, + configPath: ".comet/config.yaml", + supportedFixes: [ + "increase native.snapshot.max_total_bytes or native.snapshot.max_duration_ms", + "add an explicit native.snapshot.exclude pattern for data outside implementation scope" + ], + requiredAction: "resolve-native-baseline" + }, + error: { code: "baseline-incomplete", message: error.message } + }; + } + if (error instanceof Error) { + const systemCode = error.code; + if (systemCode && (/* @__PURE__ */ new Set(["EACCES", "EPERM", "EIO", "EMFILE", "ENFILE", "ENOSPC", "EROFS"])).has(systemCode)) { + return { + command, + exitCode: 70, + error: { code: "internal", message: error.message } + }; + } + const conflict = /\b(lock|transaction|conflict|occupied|incomplete|recovery)\b/iu.test( + error.message + ); + return { + command, + exitCode: conflict ? 73 : 65, + error: { code: conflict ? "conflict" : "invalid-data", message: error.message } + }; + } + return { + command, + exitCode: 70, + error: { code: "internal", message: String(error) } + }; +} +function render(result2, json) { + if (json) { + return { + exitCode: result2.exitCode, + stdout: JSON.stringify({ + command: result2.command, + exitCode: result2.exitCode, + ...result2.data === void 0 ? {} : { data: result2.data }, + ...result2.error === void 0 ? {} : { error: result2.error } + }) + "\n" + }; + } + if (result2.error) { + return { exitCode: result2.exitCode, stderr: result2.error.message }; + } + return { exitCode: result2.exitCode, stdout: result2.text }; +} +async function runNativeCli(argv) { + const args = [...argv]; + const json = args.includes("--json"); + let explicitProjectRoot; + let command = args[0] ?? null; + try { + takeFlag(args, "--json"); + explicitProjectRoot = takeOption(args, "--project-root"); + command = args[0] ?? null; + return render(await dispatch(args, explicitProjectRoot), json); + } catch (error) { + return render(errorResult(command, error), json); + } +} + +// domains/comet-native/native-cli-entry.ts +async function main(argv = process.argv.slice(2)) { + const result2 = await runNativeCli(argv); + if (result2.stdout) process.stdout.write(result2.stdout); + if (result2.stderr) { + process.stderr.write(result2.stderr + (result2.stderr.endsWith("\n") ? "" : "\n")); + } + return result2.exitCode; +} +var entry = process.argv[1]; +if (entry && import.meta.url === pathToFileURL(entry).href) { + void main().then((exitCode) => { + process.exitCode = exitCode; + }); +} +export { + main +}; diff --git a/.claude/skills/comet/SKILL.md b/.claude/skills/comet/SKILL.md new file mode 100644 index 0000000..e0809a1 --- /dev/null +++ b/.claude/skills/comet/SKILL.md @@ -0,0 +1,28 @@ +--- +name: comet +description: "当用户调用 /comet 或需要恢复 active Comet change 时,按项目配置路由到永久的 Comet Native 或 Comet Classic 入口。" +--- + +# Comet 入口 + +`/comet` 只负责选择入口,不包含任何一种工作流的执行方法。 + +1. 先在当前项目尝试 PATH 中安装的 Comet CLI: + + ```text + comet workflow resolve . --json + ``` + +2. **只有**宿主明确报告 `command not found`、`executable not found` 或 `ENOENT`,能够证明 `comet` 不在 PATH 时,才从当前 `SKILL.md` 所在目录定位 ``,运行自带入口 runtime: + + ```text + node /scripts/comet-entry-runtime.mjs . --json + ``` + + CLI 已启动但返回非零、配置解析失败、输出不是 JSON 或字段无效,都不得使用 bundled runtime 重试;停止并原样说明错误,不要回退或猜测。 +3. 解析 JSON。只接受 `schema: comet.workflow-resolution.v1`,且 `skill` 必须是下列两个值之一。 +4. 只按返回的 `skill` 调用一个入口,并把用户原始请求完整交给它: + - `comet-native` → `/comet-native` + - `comet-classic` → `/comet-classic` + +不根据任务大小、文件数量、活跃 change 或模型判断改选另一套工作流。Native 与 Classic 的 change、状态和产物始终彼此独立。 diff --git a/.claude/skills/comet/scripts/comet-entry-runtime.mjs b/.claude/skills/comet/scripts/comet-entry-runtime.mjs new file mode 100644 index 0000000..1775481 --- /dev/null +++ b/.claude/skills/comet/scripts/comet-entry-runtime.mjs @@ -0,0 +1,7946 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path5) { + const ctrl = callVisitor(key, node, visitor, path5); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path5, ctrl); + return visit_(key, ctrl, visitor, path5); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path5 = Object.freeze(path5.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path5); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path5 = Object.freeze(path5.concat(node)); + const ck = visit_("key", node.key, visitor, path5); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path5); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path5) { + const ctrl = await callVisitor(key, node, visitor, path5); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path5, ctrl); + return visitAsync_(key, ctrl, visitor, path5); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path5 = Object.freeze(path5.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path5); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path5 = Object.freeze(path5.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path5); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path5); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path5) { + if (typeof visitor === "function") + return visitor(key, node, path5); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path5); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path5); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path5); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path5); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path5); + return void 0; + } + function replaceNode(key, path5, node) { + const parent = path5[path5.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path5, value) { + let v = value; + for (let i = path5.length - 1; i >= 0; --i) { + const k = path5[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path5) => path5 == null || typeof path5 === "object" && !!path5[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path5, value) { + if (isEmptyPath(path5)) + this.add(value); + else { + const [key, ...rest] = path5; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path5) { + const [key, ...rest] = path5; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path5, keepScalar) { + const [key, ...rest] = path5; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path5) { + const [key, ...rest] = path5; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path5, value) { + const [key, ...rest] = path5; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify2(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify2.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify2.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify2 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify2.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify3 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify3(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify2.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify2.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp.intTime, + timestamp.floatTime, + timestamp.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify2.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify2.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify2.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path5, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path5, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path5) { + if (Collection.isEmptyPath(path5)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path5) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path5, keepScalar) { + if (Collection.isEmptyPath(path5)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path5, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path5) { + if (Collection.isEmptyPath(path5)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path5) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path5, value) { + if (Collection.isEmptyPath(path5)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path5), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path5, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify2 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path5) => { + let item = cst; + for (const [field, index] of path5) { + const tok = item?.[field]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path5) => { + const parent = visit.itemAtPath(cst, path5.slice(0, -1)); + const field = path5[path5.length - 1][0]; + const coll = parent?.[field]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path5, item, visitor) { + let ctrl = visitor(item, path5); + if (typeof ctrl === "symbol") + return ctrl; + for (const field of ["key", "value"]) { + const token = item[field]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path5.concat([[field, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field === "key") + ctrl = ctrl(item, path5); + } + } + return typeof ctrl === "function" ? ctrl(item, path5) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs4 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs4, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs4); + } else { + Object.assign(it, { key: fs4, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs4 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs4, sep: [] }); + else if (it.sep) + this.stack.push(fs4); + else + Object.assign(it, { key: fs4, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument3(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument3(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify2(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument3; + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/comet-entry/entry-runtime.ts +import path4 from "path"; + +// domains/comet-native/native-config.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { promises as fs3 } from "fs"; +import path3 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml = __toESM(require_dist(), 1); + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// domains/comet-native/native-protected-file.ts +import { createHash } from "node:crypto"; +import { constants as fsConstants, promises as fs } from "node:fs"; +import path from "node:path"; +import { TextDecoder } from "node:util"; +function isInside(parent, target) { + const relative = path.relative(parent, target); + return relative === "" || !path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`); +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity(directory, label) { + const stat = await fs.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory, label) { + const lexicalRoot = path.resolve(root); + const lexicalDirectory = path.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path.relative(lexicalRoot, lexicalDirectory).split(path.sep).filter(Boolean)) { + await verifyDirectoryChain(chain, label); + cursor = path.join(cursor, segment); + const identity = await captureDirectoryIdentity(cursor, label); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain(chain, label); + return chain; +} +async function verifyDirectoryChain(chain, label) { + for (const identity of chain) { + const stat = await fs.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit(options.maxBytes); + const file = path.resolve(options.file); + const chain = await captureDirectoryChain(options.root, path.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity(path.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain(chain, options.label); + const before = await fs.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs.realpath(file); + if (!isInside(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + const handle = await fs.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs.lstat(file), + fs.realpath(file) + ]); + await verifyDirectoryChain(chain, options.label); + await verifyDirectoryChain(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity(beforeIdentity, opened) || !sameFileIdentity(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs.lstat(file), + fs.realpath(file) + ]); + await verifyDirectoryChain(chain, options.label); + await verifyDirectoryChain(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity(beforeIdentity, afterHandle) || !sameFileIdentity(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(snapshot.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot, text }; +} + +// domains/comet-native/native-paths.ts +import { promises as fs2 } from "fs"; +import path2 from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs2.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs2.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path2.resolve(startPath); + try { + if (!(await fs2.stat(cursor)).isDirectory()) cursor = path2.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path2.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path2.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path2.join(cursor, ".git"))) return cursor; + const parent = path2.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path2.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized = path2.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized === ".." || normalized.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized === "" ? "." : normalized; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot = record(value, "native.snapshot"); + rejectUnknown(snapshot, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path3.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs3.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} + +// domains/comet-entry/resolve-entry.ts +function configuredResolution(workflow) { + return { + workflow, + skill: workflow === "native" ? "comet-native" : "comet-classic", + source: "project-config" + }; +} +async function resolveCometEntry(startPath) { + const projectRoot = await discoverNativeProject(startPath); + const config = await readProjectConfig(projectRoot); + if (!config) { + return { + workflow: "classic", + skill: "comet-classic", + source: "legacy-fallback" + }; + } + return configuredResolution(config.default_workflow); +} + +// domains/comet-entry/workflow-resolution.ts +var COMET_WORKFLOW_RESOLUTION_SCHEMA = "comet.workflow-resolution.v1"; +async function resolveCometWorkflowResolution(startPath) { + return { + schema: COMET_WORKFLOW_RESOLUTION_SCHEMA, + ...await resolveCometEntry(startPath) + }; +} +function formatCometWorkflowResolution(resolution) { + return [ + `workflow: ${resolution.workflow}`, + `skill: ${resolution.skill}`, + `source: ${resolution.source}` + ].join("\n"); +} + +// domains/comet-entry/entry-runtime.ts +var USAGE = "Usage: comet-entry-runtime [path] [--json]"; +function parseEntryRuntimeArgs(args) { + let help = false; + let json = false; + let targetPath = null; + for (const arg of args) { + if (arg === "--help" || arg === "-h") { + help = true; + continue; + } + if (arg === "--json") { + json = true; + continue; + } + if (arg.startsWith("-")) { + throw new Error(`Unknown option: ${arg}`); + } + if (targetPath !== null) { + throw new Error(`Unexpected argument: ${arg}`); + } + targetPath = arg; + } + return { help, json, targetPath: path4.resolve(targetPath ?? ".") }; +} +async function runCometEntryRuntime(args, io = { + stdout: (value) => process.stdout.write(value), + stderr: (value) => process.stderr.write(value) +}) { + let parsed; + try { + parsed = parseEntryRuntimeArgs(args); + } catch (error) { + io.stderr(`${error instanceof Error ? error.message : String(error)} +${USAGE} +`); + return 64; + } + if (parsed.help) { + io.stdout(`${USAGE} +`); + return 0; + } + try { + const resolution = await resolveCometWorkflowResolution(parsed.targetPath); + io.stdout( + parsed.json ? `${JSON.stringify(resolution, null, 2)} +` : `${formatCometWorkflowResolution(resolution)} +` + ); + return 0; + } catch (error) { + io.stderr(`${error instanceof Error ? error.message : String(error)} +`); + return 65; + } +} + +// domains/comet-entry/entry-runtime-entry.ts +process.exitCode = await runCometEntryRuntime(process.argv.slice(2)); diff --git a/.claude/skills/comet/scripts/comet-hook-router.mjs b/.claude/skills/comet/scripts/comet-hook-router.mjs new file mode 100644 index 0000000..c1c83b9 --- /dev/null +++ b/.claude/skills/comet/scripts/comet-hook-router.mjs @@ -0,0 +1,10525 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __esm = (fn, res, err) => function __init() { + if (err) throw err[0]; + try { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; + } catch (e) { + throw err = [e], e; + } +}; +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path14) { + const ctrl = callVisitor(key, node, visitor, path14); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path14, ctrl); + return visit_(key, ctrl, visitor, path14); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path14 = Object.freeze(path14.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path14); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path14 = Object.freeze(path14.concat(node)); + const ck = visit_("key", node.key, visitor, path14); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path14); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path14) { + const ctrl = await callVisitor(key, node, visitor, path14); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path14, ctrl); + return visitAsync_(key, ctrl, visitor, path14); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path14 = Object.freeze(path14.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path14); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path14 = Object.freeze(path14.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path14); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path14); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path14) { + if (typeof visitor === "function") + return visitor(key, node, path14); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path14); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path14); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path14); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path14); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path14); + return void 0; + } + function replaceNode(key, path14, node) { + const parent = path14[path14.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path14, value) { + let v = value; + for (let i = path14.length - 1; i >= 0; --i) { + const k = path14[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path14) => path14 == null || typeof path14 === "object" && !!path14[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path14, value) { + if (isEmptyPath(path14)) + this.add(value); + else { + const [key, ...rest] = path14; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path14) { + const [key, ...rest] = path14; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path14, keepScalar) { + const [key, ...rest] = path14; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path14) { + const [key, ...rest] = path14; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path14, value) { + const [key, ...rest] = path14; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify3(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify3.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify3.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify3 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify3.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify4 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify4(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify3.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify3.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp.intTime, + timestamp.floatTime, + timestamp.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify3.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify3.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify3.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document2 = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path14, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path14, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path14) { + if (Collection.isEmptyPath(path14)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path14) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path14, keepScalar) { + if (Collection.isEmptyPath(path14)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path14, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path14) { + if (Collection.isEmptyPath(path14)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path14) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path14, value) { + if (Collection.isEmptyPath(path14)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path14), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path14, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document2 = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document2.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document2 = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document2.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify3 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path14) => { + let item = cst; + for (const [field2, index] of path14) { + const tok = item?.[field2]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path14) => { + const parent = visit.itemAtPath(cst, path14.slice(0, -1)); + const field2 = path14[path14.length - 1][0]; + const coll = parent?.[field2]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path14, item, visitor) { + let ctrl = visitor(item, path14); + if (typeof ctrl === "symbol") + return ctrl; + for (const field2 of ["key", "value"]) { + const token = item[field2]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path14.concat([[field2, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field2 === "key") + ctrl = ctrl(item, path14); + } + } + return typeof ctrl === "function" ? ctrl(item, path14) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs14 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs14, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs14); + } else { + Object.assign(it, { key: fs14, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs14 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs14, sep: [] }); + else if (it.sep) + this.stack.push(fs14); + else + Object.assign(it, { key: fs14, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document2 = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument6(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument6(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify3(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document2.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument6; + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document2 = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document2.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/engine/state.ts +var state_exports = {}; +__export(state_exports, { + RUN_STATE_FILE: () => RUN_STATE_FILE, + applyRunStateToDocument: () => applyRunStateToDocument, + readRunState: () => readRunState, + removeRunState: () => removeRunState, + runStateFromDocument: () => runStateFromDocument, + writeRunState: () => writeRunState +}); +import { randomUUID as randomUUID2 } from "crypto"; +import { promises as fs4 } from "fs"; +import path4 from "path"; +function requiredString(doc, key) { + const value = doc[key]; + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Invalid Run state: ${key} must be a non-empty string`); + } + return value; +} +function requiredRunReference(doc, key) { + const value = requiredString(doc, key); + if (path4.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Run state: ${key} must stay inside the change directory`); + } + return value; +} +function retries(doc) { + const raw = doc.run_retries ?? "{}"; + let value; + try { + value = typeof raw === "string" ? JSON.parse(raw) : raw; + } catch (error) { + throw new Error("Invalid Run state: run_retries must be a JSON object", { cause: error }); + } + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: run_retries must be a JSON object"); + } + for (const count of Object.values(value)) { + if (!Number.isInteger(count) || Number(count) < 0) { + throw new Error("Invalid Run state: retry counts must be non-negative integers"); + } + } + return value; +} +function runStateFromDocument(doc) { + if (!doc.run_id) return null; + const runId = requiredString(doc, "run_id"); + const skill = requiredString(doc, "skill"); + const skillVersion = requiredString(doc, "skill_version"); + const skillHash = requiredString(doc, "skill_hash"); + const pendingRef = requiredRunReference(doc, "pending_ref"); + const trajectoryRef = requiredRunReference(doc, "trajectory_ref"); + const contextRef = requiredRunReference(doc, "context_ref"); + const artifactsRef = requiredRunReference(doc, "artifacts_ref"); + const checkpointRef = requiredRunReference(doc, "checkpoint_ref"); + const iteration = Number(doc.iteration); + if (!Number.isInteger(iteration) || iteration < 0) { + throw new Error("Invalid Run state: iteration must be a non-negative integer"); + } + if (doc.orchestration !== "deterministic" && doc.orchestration !== "adaptive") { + throw new Error("Invalid Run state: orchestration must be deterministic or adaptive"); + } + if (doc.run_status !== "running" && doc.run_status !== "waiting" && doc.run_status !== "completed" && doc.run_status !== "failed") { + throw new Error("Invalid Run state: run_status is invalid"); + } + return { + runId, + skill, + skillVersion, + skillHash, + orchestration: doc.orchestration, + currentStep: field(doc, "current_step"), + iteration, + pending: field(doc, "pending"), + pendingRef, + trajectoryRef, + contextRef, + artifactsRef, + checkpointRef, + status: doc.run_status, + retries: retries(doc) + }; +} +function applyRunStateToDocument(doc, state) { + if (state) { + doc.run_id = state.runId; + } else { + delete doc.run_id; + } +} +function runStateToJson(state) { + return { + runId: state.runId, + skill: state.skill, + skillVersion: state.skillVersion, + skillHash: state.skillHash, + orchestration: state.orchestration, + currentStep: state.currentStep, + iteration: state.iteration, + pending: state.pending, + pendingRef: state.pendingRef, + trajectoryRef: state.trajectoryRef, + contextRef: state.contextRef, + artifactsRef: state.artifactsRef, + checkpointRef: state.checkpointRef, + status: state.status, + retries: state.retries + }; +} +function runStateFromJson(json) { + const doc = { + run_id: json.runId, + skill: json.skill, + skill_version: json.skillVersion, + skill_hash: json.skillHash, + orchestration: json.orchestration, + current_step: json.currentStep, + iteration: json.iteration, + pending: json.pending, + pending_ref: json.pendingRef, + trajectory_ref: json.trajectoryRef, + context_ref: json.contextRef, + artifacts_ref: json.artifactsRef, + checkpoint_ref: json.checkpointRef, + run_status: json.status, + run_retries: JSON.stringify(json.retries) + }; + return runStateFromDocument(doc); +} +async function readRunState(changeDir) { + const file = path4.join(changeDir, RUN_STATE_FILE); + let raw; + try { + raw = await fs4.readFile(file, "utf8"); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const json = JSON.parse(raw); + return runStateFromJson(json); +} +async function writeRunState(changeDir, state) { + await fs4.mkdir(path4.join(changeDir, ".comet"), { recursive: true }); + const file = path4.join(changeDir, RUN_STATE_FILE); + const temporary = path4.join(changeDir, ".comet", `run-state.${randomUUID2()}.tmp`); + await fs4.writeFile(temporary, JSON.stringify(runStateToJson(state), null, 2), "utf8"); + await fs4.rename(temporary, file); +} +async function removeRunState(changeDir) { + await fs4.rm(path4.join(changeDir, RUN_STATE_FILE), { force: true }); +} +var field, RUN_STATE_FILE; +var init_state = __esm({ + "domains/engine/state.ts"() { + "use strict"; + field = (doc, key) => { + const value = doc[key]; + return value === null || value === void 0 ? null : String(value); + }; + RUN_STATE_FILE = ".comet/run-state.json"; + } +}); + +// domains/comet-entry/hook-router-entry.ts +import path13 from "path"; +import { promises as fs13 } from "fs"; + +// domains/comet-native/native-paths.ts +import { promises as fs } from "fs"; +import path from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +function inside(parent, target) { + const relative = path.relative(parent, target); + return relative === "" || !path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`); +} +async function physicalPath(target) { + const missing = []; + let cursor = target; + while (!await isFileOrDirectory(cursor)) { + const parent = path.dirname(cursor); + if (parent === cursor) break; + missing.push(path.basename(cursor)); + cursor = parent; + } + const existing = await fs.realpath(cursor); + return path.resolve(existing, ...missing.reverse()); +} +async function isSymbolicLink(target) { + try { + return (await fs.lstat(target)).isSymbolicLink(); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path.resolve(startPath); + try { + if (!(await fs.stat(cursor)).isDirectory()) cursor = path.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path.join(cursor, ".git"))) return cursor; + const parent = path.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized2 = path.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized2 === ".." || normalized2.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized2 === "" ? "." : normalized2; +} +async function resolveArtifactRoot(projectRoot, value) { + const normalized2 = normalizeArtifactRootRef(value); + const lexical = path.resolve(projectRoot, ...normalized2.split("/")); + const physicalProject = await fs.realpath(projectRoot); + const physicalTarget = await physicalPath(lexical); + if (!inside(physicalProject, physicalTarget)) { + throw new Error("native.artifact_root resolves outside the project root"); + } + return lexical; +} +async function nativeProjectPaths(projectRoot, artifactRootRef) { + const normalized2 = normalizeArtifactRootRef(artifactRootRef); + const artifactRoot = await resolveArtifactRoot(projectRoot, normalized2); + const nativeRoot = path.join(artifactRoot, "comet"); + if (await isSymbolicLink(nativeRoot)) { + throw new Error("The configured Native comet root must not be a symbolic link"); + } + const [physicalArtifactRoot, physicalNativeRoot] = await Promise.all([ + physicalPath(artifactRoot), + physicalPath(nativeRoot) + ]); + if (!inside(physicalArtifactRoot, physicalNativeRoot)) { + throw new Error("The configured Native comet root resolves outside its artifact root"); + } + return { + projectRoot: path.resolve(projectRoot), + configFile: path.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")), + artifactRoot, + artifactRootRef: normalized2, + nativeRoot, + specsDir: path.join(nativeRoot, "specs"), + changesDir: path.join(nativeRoot, "changes"), + archiveDir: path.join(nativeRoot, "archive"), + runtimeDir: path.join(nativeRoot, "runtime"), + locksDir: path.join(nativeRoot, "runtime", "locks"), + transactionsDir: path.join(nativeRoot, "runtime", "transactions") + }; +} +function isInsidePath(parent, target) { + return inside(path.resolve(parent), path.resolve(target)); +} +async function resolveContainedNativePath(root, target) { + const lexicalRoot = path.resolve(root); + const lexicalTarget = path.resolve(target); + if (!inside(lexicalRoot, lexicalTarget)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + if (await isSymbolicLink(lexicalRoot)) { + throw new Error(`Native root must not be a symbolic link: ${root}`); + } + const [physicalRoot, physicalTarget] = await Promise.all([ + physicalPath(lexicalRoot), + physicalPath(lexicalTarget) + ]); + if (!inside(physicalRoot, physicalTarget)) { + throw new Error(`Path resolves outside the Native root: ${target}`); + } + return lexicalTarget; +} + +// domains/comet-entry/hook-adapter.ts +import { readFileSync } from "fs"; +var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "applypatch", + "create", + "createfile", + "deletefile", + "edit", + "editfile", + "patch", + "strreplaceeditor", + "write", + "writefile", + "writefiletool" +]); +var NON_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "glob", + "grep", + "listfiles", + "read", + "readfile", + "search", + "view" +]); +var SINGULAR_PATH_KEYS = ["file_path", "filePath", "path", "target_file", "targetFile"]; +var PLURAL_PATH_KEYS = ["file_paths", "filePaths", "paths", "files", "targets"]; +var NESTED_TARGET_KEYS = ["operations", "edits"]; +var PATCH_KEYS = ["patch", "diff", "patchText", "patch_text", "changes"]; +var COMET_HOOK_PLATFORM_IDS = /* @__PURE__ */ new Set([ + "claude", + "codex", + "windsurf", + "github-copilot", + "gemini", + "amazon-q", + "qwen", + "kiro", + "codebuddy", + "qoder" +]); +function isRecord(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function normalizedToolName(value) { + return value.toLowerCase().replace(/[^a-z0-9]+/gu, ""); +} +function readToolName(input) { + for (const key of ["tool_name", "toolName", "tool", "name"]) { + const value = input[key]; + if (typeof value === "string" && value.trim()) return value.trim(); + } + return null; +} +function parseJsonValue(value) { + if (typeof value !== "string") return value; + const source = value.trim(); + if (!source.startsWith("{") && !source.startsWith("[")) return value; + try { + return JSON.parse(source); + } catch { + return value; + } +} +function readToolArguments(input) { + for (const key of ["tool_input", "toolInput", "toolArgs", "tool_args", "arguments"]) { + if (input[key] !== void 0) return parseJsonValue(input[key]); + } + return input; +} +function patchTargets(source) { + const targets = []; + const patterns = [ + /^\*\*\* (?:Add|Update|Delete) File:\s+(.+?)\s*$/gmu, + /^\+\+\+\s+(?:b\/)?(.+?)\s*$/gmu + ]; + for (const pattern of patterns) { + for (const match of source.matchAll(pattern)) { + const target = match[1]?.trim(); + if (target && target !== "/dev/null") targets.push(target); + } + } + return targets; +} +function addTarget(targets, value) { + if (typeof value === "string") { + const target = value.trim(); + if (target) targets.push(target); + return; + } + if (Array.isArray(value)) { + for (const entry of value) addTarget(targets, entry); + return; + } + if (!isRecord(value)) return; + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, value[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, value[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, value[key]); +} +function collectTargets(input, args) { + const targets = []; + const records = [args, input].filter(isRecord); + for (const record3 of records) { + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, record3[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, record3[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, record3[key]); + for (const key of PATCH_KEYS) { + const value = record3[key]; + if (typeof value === "string") targets.push(...patchTargets(value)); + } + } + if (typeof args === "string") targets.push(...patchTargets(args)); + return [...new Set(targets)]; +} +function parseCometHookRequest(source, filePath) { + if (filePath?.trim()) { + return { intent: "write", targets: [filePath.trim()], toolName: null }; + } + if (!source.trim()) return { intent: "unknown", targets: [], toolName: null }; + let input; + try { + input = JSON.parse(source); + } catch { + return { intent: "unknown", targets: [], toolName: null }; + } + if (!isRecord(input)) return { intent: "unknown", targets: [], toolName: null }; + const toolName = readToolName(input); + const targets = collectTargets(input, readToolArguments(input)); + if (toolName && WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName + }; + } + if (toolName && NON_WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { intent: "non-write", targets: [], toolName }; + } + if (toolName) return { intent: "unknown", targets, toolName }; + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName: null + }; +} +function readCometHookRequest() { + const filePath = process.env.FILE_PATH; + if (filePath?.trim()) return parseCometHookRequest("", filePath); + if (process.stdin.isTTY) return parseCometHookRequest("", filePath); + try { + return parseCometHookRequest(readFileSync(0, "utf8"), filePath); + } catch { + return parseCometHookRequest("", filePath); + } +} +function renderCometHookDecision(platformId, decision) { + if (!COMET_HOOK_PLATFORM_IDS.has(platformId)) { + return { + exitCode: 64, + stdout: "", + stderr: `Unsupported Comet Hook platform: ${platformId} +` + }; + } + if (platformId === "github-copilot") { + return { + exitCode: 0, + stdout: decision.allowed ? "{}\n" : `${JSON.stringify({ + permissionDecision: "deny", + permissionDecisionReason: decision.reason + })} +`, + stderr: "" + }; + } + if (decision.allowed) return { exitCode: 0, stdout: "", stderr: "" }; + return { exitCode: 2, stdout: "", stderr: `${decision.reason} +` }; +} + +// domains/comet-classic/classic-hook-guard.ts +import { existsSync, promises as fs7, readFileSync as readFileSync2 } from "fs"; +import path7 from "path"; + +// domains/comet-classic/classic-branch-binding.ts +var import_yaml = __toESM(require_dist(), 1); +import { execFileSync } from "child_process"; +import { randomUUID } from "crypto"; +import { promises as fs2 } from "fs"; +import path2 from "path"; +function liveGitBranch(cwd) { + try { + const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"] + }).trim(); + return branch && branch !== "HEAD" ? branch : null; + } catch { + return null; + } +} +function isGitWorkTree(cwd) { + try { + return execFileSync("git", ["rev-parse", "--is-inside-work-tree"], { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"] + }).trim() === "true"; + } catch { + return false; + } +} +var BOUND_BRANCH_ISOLATIONS = ["current", "branch", "worktree"]; +function requiresBranchBinding(isolation) { + return BOUND_BRANCH_ISOLATIONS.includes(isolation); +} +function evaluateBranchBinding(input) { + if (!requiresBranchBinding(input.isolation)) return { status: "not-applicable" }; + if (input.boundBranch === null && input.currentBranch === null && input.gitWorkTree === false) { + return { status: "not-applicable" }; + } + if (input.boundBranch === null) { + return input.currentBranch === null ? { status: "unbound-detached" } : { status: "needs-heal", branch: input.currentBranch }; + } + if (input.currentBranch === input.boundBranch) return { status: "ok" }; + return { status: "drift", boundBranch: input.boundBranch, currentBranch: input.currentBranch }; +} +async function resolveBranchBinding(changeDir, options) { + const file = path2.join(changeDir, ".comet.yaml"); + const document = (0, import_yaml.parseDocument)(await fs2.readFile(file, "utf8"), { uniqueKeys: false }); + if (document.errors.length > 0) { + throw new Error(`Invalid .comet.yaml: ${document.errors[0].message}`); + } + const record3 = document.toJS() ?? {}; + const isolation = typeof record3.isolation === "string" ? record3.isolation : null; + const boundBranch = typeof record3.bound_branch === "string" && record3.bound_branch !== "" ? record3.bound_branch : null; + const bindingRequired = requiresBranchBinding(isolation); + const currentBranch = liveGitBranch(options.cwd); + const gitWorkTree = bindingRequired && boundBranch === null && currentBranch === null ? isGitWorkTree(options.cwd) : true; + const verdict = evaluateBranchBinding({ isolation, boundBranch, currentBranch, gitWorkTree }); + if (verdict.status === "needs-heal" && options.heal) { + await healBoundBranch(changeDir, verdict.branch); + return { status: "healed", branch: verdict.branch, bindingRequired, currentBranch }; + } + return { ...verdict, bindingRequired, currentBranch }; +} +async function healBoundBranch(changeDir, branch) { + const file = path2.join(changeDir, ".comet.yaml"); + const document = (0, import_yaml.parseDocument)(await fs2.readFile(file, "utf8"), { uniqueKeys: false }); + document.set("bound_branch", branch); + const temporary = `${file}.${randomUUID()}.tmp`; + try { + await fs2.writeFile(temporary, document.toString(), "utf8"); + await fs2.rename(temporary, file); + } catch (error) { + await fs2.rm(temporary, { force: true }); + throw error; + } +} +function branchLabel(currentBranch) { + return currentBranch ?? "detached HEAD"; +} +function driftStaleReason(change, boundBranch, currentBranch) { + return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'`; +} +function unboundDetachedMessage(change) { + return `change '${change}' uses a branch-bound workspace mode but has no bound branch and HEAD is detached; checkout a branch first before continuing.`; +} + +// domains/comet-classic/classic-current-change.ts +import { promises as fs6 } from "fs"; +import path6 from "path"; + +// domains/comet-entry/current-selection.ts +import path3 from "path"; + +// platform/fs/race-safe-read.ts +import { constants as fsConstants, promises as fs3 } from "fs"; + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function hasComparableFileObject(left, right) { + return hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev) && hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// platform/fs/race-safe-read.ts +var RaceSafeReadError = class extends Error { + reason; + constructor(reason, message, options) { + super(message, options); + this.name = "RaceSafeReadError"; + this.reason = reason; + } +}; +function birthtimeOf(stat) { + return "birthtimeNs" in stat && typeof stat.birthtimeNs === "bigint" ? stat.birthtimeNs : stat.birthtimeMs; +} +function ctimeOf(stat) { + return "ctimeNs" in stat && typeof stat.ctimeNs === "bigint" ? stat.ctimeNs : stat.ctimeMs; +} +function identityOf(stat) { + return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) }; +} +function sameStatIdentity(left, right) { + const leftObject = identityOf(left); + const rightObject = identityOf(right); + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && birthtimeOf(left) === birthtimeOf(right) && ctimeOf(left) === ctimeOf(right) && left.size === right.size; +} +async function readFileRaceSafe(file, maxBytes, options = {}) { + if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) { + throw new Error("race-safe read byte limit must be a positive integer"); + } + const label = options.label ?? "file"; + const bigint = options.bigint === true; + const before = await fs3.lstat(file, { bigint }); + if (!before.isFile() || before.isSymbolicLink()) { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`); + } + if (BigInt(before.size) > BigInt(maxBytes)) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + const beforeRealPath = await fs3.realpath(file); + await options.verify?.("pre-open", { realPath: beforeRealPath, identity: identityOf(before) }); + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + let handle; + try { + handle = await fs3.open(file, flags); + } catch (error) { + if (error.code === "ELOOP") { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`, { + cause: error + }); + } + throw error; + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat({ bigint }), + fs3.lstat(file, { bigint }), + fs3.realpath(file) + ]); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameStatIdentity(before, opened) || !sameStatIdentity(before, pathAfterOpen)) { + throw new RaceSafeReadError("changed", `${label} changed while opening`); + } + await options.verify?.("post-open", { + realPath: realPathAfterOpen, + identity: identityOf(opened) + }); + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + for (; ; ) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat({ bigint }), + fs3.lstat(file, { bigint }), + fs3.realpath(file) + ]); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameStatIdentity(before, afterHandle) || !sameStatIdentity(before, afterPath)) { + throw new RaceSafeReadError("changed", `${label} changed while reading`); + } + await options.verify?.("post-read", { + realPath: afterRealPath, + identity: identityOf(afterHandle) + }); + return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath }; + } finally { + await handle.close(); + } +} + +// domains/comet-entry/current-selection.ts +var COMET_CURRENT_SELECTION_SCHEMA = "comet.selection.v2"; +var COMET_CURRENT_SELECTION_MAX_BYTES = 16 * 1024; +function cometCurrentSelectionFile(projectRoot) { + return path3.join(projectRoot, ".comet", "current-change.json"); +} +function isRecord2(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function validBranch(value) { + return value === null || typeof value === "string"; +} +function parseSelection(source) { + let value; + try { + value = JSON.parse(source); + } catch (error) { + throw new Error( + `current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + if (!isRecord2(value)) { + throw new Error("current change selection must be a JSON object"); + } + if (value.version === 1) { + if (typeof value.change !== "string") { + throw new Error("legacy current change selection change must be a string"); + } + if (value.branch !== void 0 && !validBranch(value.branch)) { + throw new Error("legacy current change selection branch must be a string or null"); + } + return { + selection: { + schema: COMET_CURRENT_SELECTION_SCHEMA, + workflow: "classic", + change: value.change, + branch: value.branch ?? null + }, + legacy: true + }; + } + if (value.schema !== COMET_CURRENT_SELECTION_SCHEMA) { + throw new Error(`current change selection schema must be ${COMET_CURRENT_SELECTION_SCHEMA}`); + } + if (value.workflow !== "native" && value.workflow !== "classic") { + throw new Error("current change selection workflow must be native or classic"); + } + if (typeof value.change !== "string") { + throw new Error("current change selection change must be a string"); + } + if (!validBranch(value.branch)) { + throw new Error("current change selection branch must be a string or null"); + } + if (value.workflow === "native" && value.branch !== null) { + throw new Error("Native current change selection branch must be null"); + } + return { selection: value, legacy: false }; +} +async function readCometCurrentSelection(projectRoot) { + let source; + try { + const file = cometCurrentSelectionFile(projectRoot); + const { bytes } = await readFileRaceSafe(file, COMET_CURRENT_SELECTION_MAX_BYTES, { + label: "current change selection" + }); + source = bytes.toString("utf8"); + } catch (error) { + if (error.code === "ENOENT") return { status: "missing" }; + throw new Error( + `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + const parsed = parseSelection(source); + return { status: "selected", ...parsed }; +} + +// domains/comet-classic/classic-paths.ts +function openSpecChangeNameError(name) { + if (!name) return "Change name cannot be empty"; + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(name)) { + return `Invalid change name: '${name}' +Valid format: lowercase kebab-case (a-z, 0-9, single hyphens)`; + } + if (name.includes("..")) return "Change name cannot contain '..' (path traversal not allowed)"; + return null; +} +function assertOpenSpecChangeName(name) { + const error = openSpecChangeNameError(name); + if (error) throw new Error(error); +} + +// domains/comet-classic/classic-store.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { randomUUID as randomUUID3 } from "crypto"; +import { promises as fs5 } from "fs"; +import path5 from "path"; + +// domains/comet-classic/classic-state.ts +init_state(); +var CLASSIC_PROFILES = ["full", "hotfix", "tweak"]; +var CLASSIC_MIGRATION_VERSION = 1; +var PHASES = ["open", "design", "build", "verify", "archive"]; +var ARTIFACT_LANGUAGES = ["en", "zh-CN"]; +var CONTEXT_COMPRESSION = ["off", "beta"]; +var BUILD_MODES = ["subagent-driven-development", "executing-plans", "direct"]; +var BUILD_PAUSES = ["plan-ready"]; +var SUBAGENT_DISPATCH = ["confirmed"]; +var TDD_MODES = ["tdd", "direct"]; +var REVIEW_MODES = ["off", "standard", "thorough"]; +var ISOLATIONS = ["current", "branch", "worktree"]; +var VERIFY_MODES = ["light", "full"]; +var VERIFY_RESULTS = ["pending", "pass", "fail"]; +var BRANCH_STATUSES = ["pending", "handled"]; +var ARCHIVE_CONFIRMATIONS = ["pending", "confirmed"]; +var CLASSIC_WIRE_KEYS = [ + "workflow", + "language", + "phase", + "context_compression", + "build_mode", + "build_pause", + "subagent_dispatch", + "tdd_mode", + "review_mode", + "isolation", + "bound_branch", + "verify_mode", + "auto_transition", + "base_ref", + "design_doc", + "plan", + "verify_result", + "verify_failures", + "verification_report", + "branch_status", + "created_at", + "verified_at", + "archive_confirmation", + "archived", + "direct_override", + "handoff_context", + "handoff_hash", + "classic_profile", + "classic_migration" +]; +var RUN_WIRE_KEYS = ["run_id"]; +var KNOWN_KEYS = /* @__PURE__ */ new Set([...CLASSIC_WIRE_KEYS, ...RUN_WIRE_KEYS]); +var REQUIRED_CLASSIC_KEYS = [ + "workflow", + "phase", + "design_doc", + "plan", + "build_mode", + "isolation", + "verify_mode", + "verify_result", + "verified_at", + "archived" +]; +function has(doc, key) { + return Object.prototype.hasOwnProperty.call(doc, key); +} +function nullableString(doc, key) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") return null; + if (typeof value !== "string") { + throw new Error(`Invalid Classic state: ${key} must be a string or null`); + } + return value; +} +function enumValue(doc, key, values, nullable = true) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") { + if (nullable) return null; + throw new Error(`Invalid Classic state: ${key} is required`); + } + if (typeof value !== "string" || !values.includes(value)) { + throw new Error( + `Invalid Classic state: ${key} must be one of ${values.join(", ")}${nullable ? " or null" : ""}` + ); + } + return value; +} +function booleanValue(doc, key, nullable = true) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") { + if (nullable) return null; + throw new Error(`Invalid Classic state: ${key} is required`); + } + if (typeof value !== "boolean") { + throw new Error(`Invalid Classic state: ${key} must be true or false`); + } + return value; +} +function nonNegativeInteger(doc, key, fallback = 0) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") return fallback; + if (typeof value !== "number" || !Number.isInteger(value) || value < 0) { + throw new Error(`Invalid Classic state: ${key} must be a non-negative integer`); + } + return value; +} +function relativePath(doc, key) { + const value = nullableString(doc, key); + if (value === null) return null; + if (/^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Classic state: ${key} must be a relative repository path`); + } + return value; +} +function sha256(doc, key) { + const value = nullableString(doc, key); + if (value !== null && !/^[a-f0-9]{64}$/u.test(value)) { + throw new Error(`Invalid Classic state: ${key} must be a sha256 hex digest`); + } + return value; +} +function migrationVersion(doc) { + const value = doc.classic_migration; + if (value === null || value === void 0 || value === "") return null; + if (value !== CLASSIC_MIGRATION_VERSION) { + throw new Error( + `Invalid Classic state: classic_migration must be ${CLASSIC_MIGRATION_VERSION}` + ); + } + return value; +} +function classicStateFromDocument(doc) { + const hasClassicProjection = CLASSIC_WIRE_KEYS.some((key) => has(doc, key)); + if (!hasClassicProjection) return null; + for (const key of REQUIRED_CLASSIC_KEYS) { + if (!has(doc, key)) return null; + } + return { + workflow: enumValue(doc, "workflow", CLASSIC_PROFILES, false), + language: enumValue(doc, "language", ARTIFACT_LANGUAGES), + phase: enumValue(doc, "phase", PHASES, false), + contextCompression: enumValue(doc, "context_compression", CONTEXT_COMPRESSION), + buildMode: enumValue(doc, "build_mode", BUILD_MODES), + buildPause: enumValue(doc, "build_pause", BUILD_PAUSES), + subagentDispatch: enumValue(doc, "subagent_dispatch", SUBAGENT_DISPATCH), + tddMode: enumValue(doc, "tdd_mode", TDD_MODES), + reviewMode: enumValue(doc, "review_mode", REVIEW_MODES), + isolation: enumValue(doc, "isolation", ISOLATIONS), + boundBranch: nullableString(doc, "bound_branch"), + verifyMode: enumValue(doc, "verify_mode", VERIFY_MODES), + autoTransition: booleanValue(doc, "auto_transition"), + baseRef: nullableString(doc, "base_ref"), + designDoc: relativePath(doc, "design_doc"), + plan: relativePath(doc, "plan"), + verifyResult: enumValue(doc, "verify_result", VERIFY_RESULTS, false), + verifyFailures: nonNegativeInteger(doc, "verify_failures"), + verificationReport: relativePath(doc, "verification_report"), + branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES), + createdAt: nullableString(doc, "created_at"), + verifiedAt: nullableString(doc, "verified_at"), + archiveConfirmation: enumValue(doc, "archive_confirmation", ARCHIVE_CONFIRMATIONS), + archived: booleanValue(doc, "archived", false), + directOverride: booleanValue(doc, "direct_override"), + handoffContext: relativePath(doc, "handoff_context"), + handoffHash: sha256(doc, "handoff_hash"), + classicProfile: enumValue(doc, "classic_profile", CLASSIC_PROFILES), + classicMigration: migrationVersion(doc) + }; +} +function parseClassicStateDocument(doc, run) { + let resolvedRun = run ?? null; + if (resolvedRun === null && run === void 0) { + if (doc.run_id && doc.skill) { + try { + resolvedRun = runStateFromDocument(doc); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(message.replace(/^Invalid Run state:/u, "Invalid Classic state:"), { + cause: error + }); + } + } + } + return { + classic: classicStateFromDocument(doc), + run: resolvedRun, + unknownKeys: Object.keys(doc).filter((key) => !KNOWN_KEYS.has(key)) + }; +} +function readLegacyStateSummary(doc) { + const workflowRaw = doc["workflow"]; + const phaseRaw = doc["phase"]; + const archivedRaw = doc["archived"]; + const designDocRaw = doc["design_doc"]; + return { + workflow: typeof workflowRaw === "string" && CLASSIC_PROFILES.includes(workflowRaw) ? workflowRaw : null, + phase: typeof phaseRaw === "string" && PHASES.includes(phaseRaw) ? phaseRaw : null, + archived: archivedRaw === true, + designDoc: typeof designDocRaw === "string" && designDocRaw !== "" ? designDocRaw : null, + unknownKeys: Object.keys(doc).filter((key) => !KNOWN_KEYS.has(key)) + }; +} + +// domains/comet-classic/classic-store.ts +init_state(); +function documentRecord(document) { + const value = document.toJS(); + if (value === null) return {}; + if (typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Classic state document: root must be a mapping"); + } + return value; +} +function stripLegacyRunFields(document) { + const LEGACY_RUN_KEYS = [ + "skill", + "skill_version", + "skill_hash", + "orchestration", + "current_step", + "iteration", + "pending", + "pending_ref", + "trajectory_ref", + "context_ref", + "artifacts_ref", + "checkpoint_ref", + "run_status", + "run_retries" + ]; + for (const key of LEGACY_RUN_KEYS) document.delete(key); +} +function stripLegacyCommandFields(document) { + let changed = false; + for (const key of ["build_command", "verify_command"]) { + if (document.has(key)) { + document.delete(key); + changed = true; + } + } + return changed; +} +async function readDocument(file) { + let source; + try { + source = await fs5.readFile(file, "utf8"); + } catch (error) { + if (error.code !== "ENOENT") throw error; + return new import_yaml2.Document({}); + } + const document = (0, import_yaml2.parseDocument)(source); + if (document.errors.length > 0) { + throw new Error(`Invalid Classic state document: ${document.errors[0].message}`); + } + documentRecord(document); + return document; +} +async function readClassicState(changeDir, options = {}) { + const shouldMigrate = options.migrate !== false; + const file = path5.join(changeDir, ".comet.yaml"); + const document = await readDocument(file); + let doc = documentRecord(document); + let migrated = stripLegacyCommandFields(document); + if (migrated) doc = documentRecord(document); + let run = await readRunState(changeDir); + if (!run && doc.run_id && doc.skill) { + const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports)); + run = runStateFromDocument2(doc); + if (run && shouldMigrate) { + await writeRunState(changeDir, run); + stripLegacyRunFields(document); + migrated = true; + } + } + if (migrated && shouldMigrate) { + const temporary = path5.join(changeDir, `.comet.yaml.${randomUUID3()}.tmp`); + await fs5.writeFile(temporary, document.toString(), "utf8"); + await fs5.rename(temporary, file); + } + return parseClassicStateDocument(documentRecord(document), run); +} +async function readLegacyState(changeDir) { + const document = await readDocument(path5.join(changeDir, ".comet.yaml")); + return readLegacyStateSummary(documentRecord(document)); +} + +// domains/comet-classic/classic-current-change.ts +function changeDirectory(projectRoot, changeName) { + return path6.join(projectRoot, "openspec", "changes", changeName); +} +async function validateActiveChange(projectRoot, changeName) { + assertOpenSpecChangeName(changeName); + const changeDir = changeDirectory(projectRoot, changeName); + try { + await fs6.access(path6.join(changeDir, ".comet.yaml")); + } catch (error) { + if (error.code === "ENOENT") { + throw new Error( + `Cannot select current change '${changeName}': active change state not found`, + { + cause: error + } + ); + } + throw error; + } + const projection = await readClassicState(changeDir, { migrate: false }); + if (!projection.classic) { + throw new Error(`Cannot select current change '${changeName}': Classic state is incomplete`); + } + if (projection.classic.archived) { + throw new Error(`Cannot select current change '${changeName}': change is archived`); + } +} +async function resolveCurrentChange(projectRoot) { + let current; + try { + current = await readCometCurrentSelection(projectRoot); + } catch (error) { + return { + status: "stale", + reason: error instanceof Error ? error.message : String(error) + }; + } + if (current.status === "missing") return { status: "missing" }; + if (current.selection.workflow !== "classic") { + return { + status: "stale", + reason: `current change '${current.selection.change}' belongs to Native, not Classic` + }; + } + const selection = current.selection; + try { + await validateActiveChange(projectRoot, selection.change); + } catch (error) { + return { + status: "stale", + reason: error instanceof Error ? error.message : String(error) + }; + } + const outcome = await resolveBranchBinding(changeDirectory(projectRoot, selection.change), { + heal: false, + cwd: projectRoot + }); + if (outcome.status === "drift") { + return { + status: "stale", + reason: driftStaleReason(selection.change, outcome.boundBranch, outcome.currentBranch) + }; + } + if (outcome.status === "unbound-detached") { + return { status: "stale", reason: unboundDetachedMessage(selection.change) }; + } + if (outcome.status === "ok") return { status: "selected", selection }; + if (selection.branch !== null && outcome.currentBranch !== selection.branch) { + return { + status: "stale", + reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${outcome.currentBranch ?? "detached HEAD"}'` + }; + } + return { status: "selected", selection }; +} + +// domains/comet-classic/classic-hook-guard.ts +function result(exitCode, message) { + return { exitCode, stderr: message + "\n" }; +} +function allowed(message) { + return result(0, `[COMET-HOOK] allowed: ${message}`); +} +function normalized(value) { + return value.replaceAll("\\", "/").replace(/\/+/gu, "/"); +} +function comparisonKey(value) { + const normalizedValue = normalized(value); + return process.platform === "win32" ? normalizedValue.toLowerCase() : normalizedValue; +} +function relativeToProjectRoot(target, projectRoot) { + const relative = normalized(path7.relative(projectRoot, target)); + if (relative === "") return ""; + if (relative.startsWith("../") || relative === ".." || path7.isAbsolute(relative)) return null; + return relative; +} +async function physicalPathForPossiblyMissingTarget(target) { + const resolved = path7.resolve(target); + const root = path7.parse(resolved).root; + const missingSegments = []; + let cursor = resolved; + while (cursor && cursor !== root) { + try { + const physicalBase = await fs7.realpath(cursor); + return path7.join(physicalBase, ...missingSegments.reverse()); + } catch (error) { + const code = error.code; + if (code !== "ENOENT" && code !== "ENOTDIR") throw error; + missingSegments.push(path7.basename(cursor)); + cursor = path7.dirname(cursor); + } + } + try { + const physicalRoot = await fs7.realpath(root); + return path7.join(physicalRoot, ...missingSegments.reverse()); + } catch { + return null; + } +} +async function projectRelative(target, projectRoot) { + const rawCandidate = path7.isAbsolute(target) ? target : path7.resolve(process.cwd(), target); + let candidate = normalized(rawCandidate); + const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot); + if (rootRelative !== null) return rootRelative; + try { + const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate); + const physicalRoot = await fs7.realpath(projectRoot); + if (physicalCandidate) { + const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot); + if (physicalRootRelative !== null) return physicalRootRelative; + candidate = normalized(physicalCandidate); + } + } catch { + if (!path7.isAbsolute(target)) return normalized(target).replace(/^\.\//u, ""); + } + return candidate.replace(/^\.\//u, ""); +} +async function loadGoverningChange(changeDir) { + try { + const projection = await readClassicState(changeDir, { migrate: false }); + const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort(); + if (unknownKeys.length > 0) { + throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`); + } + if (!projection.classic) throw new Error("Classic state projection is unavailable"); + return { + changeDir, + phase: projection.classic.phase, + classic: projection.classic, + archived: projection.classic.archived + }; + } catch { + const legacy = await readLegacyState(changeDir); + if (!legacy.phase) return null; + return { + changeDir, + phase: legacy.phase, + classic: null, + archived: legacy.archived + }; + } +} +async function activeChanges(projectRoot) { + const changesDir = path7.join(projectRoot, "openspec", "changes"); + const governingChanges = []; + if (!existsSync(changesDir)) return governingChanges; + for (const entry of (await fs7.readdir(changesDir, { withFileTypes: true })).sort( + (left, right) => left.name.localeCompare(right.name) + )) { + if (!entry.isDirectory() || entry.name === "archive") continue; + const changeDir = path7.join(changesDir, entry.name); + if (!existsSync(path7.join(changeDir, ".comet.yaml"))) continue; + const governing = await loadGoverningChange(changeDir); + if (!governing || governing.archived) continue; + governingChanges.push(governing); + } + return governingChanges; +} +async function listActiveClassicHookChanges(projectRoot) { + return (await activeChanges(projectRoot)).map((change) => ({ + workflow: "classic", + name: governingChangeName(change), + phase: change.phase + })); +} +function isSuperpowersArtifactPath(relativePath2) { + return comparisonKey(relativePath2).startsWith("docs/superpowers/"); +} +var SUPERPOWERS_ARTIFACT_SLOTS = [ + { + prefix: "docs/superpowers/specs/", + field: "designDoc", + wireField: "design_doc", + phase: "design" + }, + { + prefix: "docs/superpowers/plans/", + field: "plan", + wireField: "plan", + phase: "build" + }, + { + prefix: "docs/superpowers/reports/", + field: "verificationReport", + wireField: "verification_report", + phase: "verify" + } +]; +function standardSuperpowersArtifactSlot(relativePath2) { + const key = comparisonKey(relativePath2); + const slot = SUPERPOWERS_ARTIFACT_SLOTS.find((candidate) => key.startsWith(candidate.prefix)); + if (!slot) return null; + const fileName = key.slice(slot.prefix.length); + if (!fileName || fileName.includes("/") || !fileName.endsWith(".md")) return null; + return slot; +} +function superpowersArtifactValue(governing, slot) { + return governing.classic?.[slot.field] ?? null; +} +function allowsFirstSuperpowersArtifactWrite(governing, slot) { + return governing.classic !== null && governing.phase === slot.phase && !superpowersArtifactValue(governing, slot); +} +function allowsSuperpowersArtifacts(governing) { + return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify"; +} +function governingChangeName(governing) { + return governing.changeDir ? path7.basename(governing.changeDir) : null; +} +var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([ + "design", + "plan", + "verify", + "verification", + "verification-report", + "report" +]); +function escapeRegex(value) { + return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); +} +function matchesRecordedSuperpowersArtifact(relativePath2, governing) { + const artifactPaths = [ + governing.classic?.designDoc, + governing.classic?.plan, + governing.classic?.verificationReport + ]; + return artifactPaths.some( + (artifactPath) => artifactPath && comparisonKey(artifactPath) === comparisonKey(relativePath2) + ); +} +function matchesSuperpowersArtifactName(relativePath2, changeName) { + const fileName = relativePath2.split("/").at(-1) ?? relativePath2; + const stem = fileName.replace(/\.[^.]+$/u, ""); + if (stem === changeName) return true; + const suffixes = [...SUPERPOWERS_ARTIFACT_SUFFIXES].map(escapeRegex).join("|"); + const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u"); + return pattern.test(stem); +} +async function superpowersArtifactGoverningChange(relativePath2, projectRoot) { + const active = await activeChanges(projectRoot); + const recorded = active.find( + (governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing) + ); + if (recorded) return { governing: recorded, match: "recorded" }; + const eligible = active.filter(allowsSuperpowersArtifacts); + const named = eligible.filter((governing) => { + const name = governingChangeName(governing); + return name !== null && matchesSuperpowersArtifactName(relativePath2, name); + }).sort( + (a, b) => (governingChangeName(b)?.length ?? 0) - (governingChangeName(a)?.length ?? 0) + )[0]; + if (named) return { governing: named, match: "named" }; + return null; +} +async function repoSourceGoverningChange(projectRoot, relativePath2, selectedChangeName) { + const active = await activeChanges(projectRoot); + if (active.length === 0) return null; + if (selectedChangeName) { + const selected = active.find( + (governing) => governingChangeName(governing) === selectedChangeName + ); + return selected ?? { + blockedResult: blockedStaleSelection( + relativePath2, + `selected change '${selectedChangeName}' is no longer active` + ) + }; + } + const current = await resolveCurrentChange(projectRoot); + if (current.status === "stale") { + return { blockedResult: blockedStaleSelection(relativePath2, current.reason) }; + } + if (current.status === "selected") { + const selected = active.find( + (governing) => governingChangeName(governing) === current.selection.change + ); + if (selected) return selected; + return { + blockedResult: blockedStaleSelection( + relativePath2, + `selected change '${current.selection.change}' is no longer active` + ) + }; + } + if (active.length === 1) { + const sole = active[0]; + if (sole.changeDir !== null) { + const outcome = await resolveBranchBinding(sole.changeDir, { + heal: false, + cwd: projectRoot + }); + const name = governingChangeName(sole) ?? "unknown"; + if (outcome.status === "drift") { + return { + blockedResult: blockedStaleSelection( + relativePath2, + driftStaleReason(name, outcome.boundBranch, outcome.currentBranch) + ) + }; + } + if (outcome.status === "unbound-detached") { + return { + blockedResult: blockedStaleSelection(relativePath2, unboundDetachedMessage(name)) + }; + } + } + return sole; + } + return { + blockedResult: blockedMultipleChanges( + relativePath2, + active.map((governing) => governingChangeName(governing)).filter(Boolean) + ) + }; +} +async function governingChange(relativePath2, projectRoot, selectedChangeName) { + const prefix = "openspec/changes/"; + if (relativePath2.startsWith(prefix)) { + const rest = relativePath2.slice(prefix.length); + const [name] = rest.split("/"); + if (name && name !== "archive") { + const changeDir = path7.join(projectRoot, "openspec", "changes", name); + const stateFile = path7.join(changeDir, ".comet.yaml"); + if (existsSync(stateFile)) { + const governing = await loadGoverningChange(changeDir); + if (governing) return governing; + return { changeDir, phase: "open", classic: null, archived: false }; + } + return { changeDir, phase: "open", classic: null, archived: false }; + } + } + if (isSuperpowersArtifactPath(relativePath2)) { + const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot); + if (superpowers?.match === "recorded") { + return { ...superpowers.governing, superpowersArtifact: "matched" }; + } + const slot = standardSuperpowersArtifactSlot(relativePath2); + if (superpowers) { + return slot ? { + ...superpowers.governing, + superpowersArtifact: allowsFirstSuperpowersArtifactWrite(superpowers.governing, slot) ? "matched" : "unmatched", + superpowersSlot: slot + } : { ...superpowers.governing, superpowersArtifact: "matched" }; + } + if (slot) { + const candidate = await repoSourceGoverningChange( + projectRoot, + relativePath2, + selectedChangeName + ); + if (!candidate || "blockedResult" in candidate) return candidate; + return { + ...candidate, + superpowersArtifact: allowsFirstSuperpowersArtifactWrite(candidate, slot) ? "matched" : "unmatched", + superpowersSlot: slot + }; + } + const active = await activeChanges(projectRoot); + const fallback = selectedChangeName ? active.find((candidate) => governingChangeName(candidate) === selectedChangeName) ?? null : active[0] ?? null; + return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null; + } + return repoSourceGoverningChange(projectRoot, relativePath2, selectedChangeName); +} +function isRootMarkdown(relativePath2) { + return !relativePath2.includes("/") && relativePath2.endsWith(".md"); +} +function isCometConfig(relativePath2) { + return relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/"); +} +function isSuperpowersWorkspace(relativePath2) { + return relativePath2 === ".superpowers" || relativePath2.startsWith(".superpowers/"); +} +function openSpecAllowed(relativePath2, phase) { + if (!relativePath2.startsWith("openspec/")) return null; + const stateFile = relativePath2.endsWith("/.comet.yaml") || relativePath2.endsWith("/.openspec.yaml"); + const proposal = relativePath2.endsWith("/proposal.md") || relativePath2.endsWith("/design.md") || relativePath2.endsWith("/tasks.md"); + const handoff = relativePath2.includes("/.comet/"); + const specs = relativePath2.includes("/specs/"); + if (phase === "open" && (proposal || stateFile || handoff || specs)) { + return `${relativePath2} (phase: open, openspec artifacts)`; + } + if (phase === "design" && (proposal || stateFile || handoff || specs)) { + return `${relativePath2} (phase: design, handoff/spec)`; + } + if (phase === "build" && (relativePath2.endsWith("/tasks.md") || stateFile || specs)) { + return `${relativePath2} (phase: build, spec/tasks)`; + } + if (phase === "verify" && (relativePath2.endsWith("/tasks.md") || stateFile)) { + return `${relativePath2} (phase: verify, tasks/state)`; + } + if (phase === "archive" && stateFile) { + return `${relativePath2} (phase: archive, state)`; + } + return null; +} +function blocked(relativePath2, phase) { + const guidance = phase === "open" ? [ + " BLOCKED: source writes are not allowed during open", + " This phase does not allow source writes", + " ALLOWED: create proposal/design/tasks artifacts and run guard", + " NEXT: finish clarification and artifacts, then run guard --apply" + ] : phase === "design" ? [ + " BLOCKED: source writes are not allowed during design", + " This phase does not allow source writes", + " ALLOWED: run brainstorming, create the Design Doc, and run guard", + " NEXT: finish the Design Doc, then run comet guard design --apply to enter build" + ] : [ + " BLOCKED: source writes are not allowed during archive", + " This phase does not allow source writes", + " ALLOWED: confirm archive state and run the archive script" + ]; + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + ` Current phase: ${phase}`, + ` Target file: ${relativePath2}`, + "", + ...guidance, + "" + ].join("\n") + ); +} +function blockedMissingDesignDoc(relativePath2) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " Current phase: build (workflow: full), but design_doc is empty", + ` Target file: ${relativePath2}`, + "", + " BLOCKED: full workflow source writes require a recorded Design Doc", + " This phase does not allow source writes until design_doc is recorded", + " NEXT: return to design, create/link the Design Doc, then run guard again", + "" + ].join("\n") + ); +} +function blockedUnmatchedSuperpowersArtifact(relativePath2, governing) { + const slot = governing.superpowersSlot; + const recorded = slot ? superpowersArtifactValue(governing, slot) : null; + const details = slot ? governing.phase !== slot.phase ? [ + ` BLOCKED: ${slot.wireField} cannot be first-written in phase ${governing.phase}`, + ` Expected phase: ${slot.phase}`, + " NEXT: resume the matching Comet phase or use an already recorded artifact path" + ] : recorded ? [ + ` BLOCKED: ${slot.wireField} is already recorded for this change`, + ` Recorded path: ${recorded}`, + " NEXT: write the recorded artifact or explicitly correct the state path" + ] : [ + " BLOCKED: standard Superpowers artifact state is incomplete", + " NEXT: validate the active change state, then retry the matching phase" + ] : [ + " BLOCKED: unmatched Superpowers artifact", + " This docs/superpowers/ path does not match any active change artifact", + " NEXT: use a recorded artifact path or a standard phase artifact directory" + ]; + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + ` Current phase: ${governing.phase}`, + ` Target file: ${relativePath2}`, + "", + ...details, + "" + ].join("\n") + ); +} +function blockedMultipleChanges(relativePath2, changeNames) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " BLOCKED: multiple active changes require a current change", + ` Target file: ${relativePath2}`, + ` Active changes: ${changeNames.join(", ")}`, + "", + " NEXT: run comet state select , then retry the source write", + "" + ].join("\n") + ); +} +function blockedStaleSelection(relativePath2, reason) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " BLOCKED: current change selection is stale or invalid", + ` Target file: ${relativePath2}`, + ` Reason: ${reason}`, + "", + " NEXT: run comet state select , then retry the source write", + "" + ].join("\n") + ); +} +async function inspectClassicHookTarget(projectRoot, target, selectedChangeName) { + const relativePath2 = await projectRelative(target, projectRoot); + if (isCometConfig(relativePath2)) { + return allowed(`${relativePath2} (whitelist: comet config)`); + } + if (relativePath2.startsWith(".claude/")) { + return allowed(`${relativePath2} (whitelist: claude config)`); + } + if (isSuperpowersWorkspace(relativePath2)) { + return allowed(`${relativePath2} (whitelist: superpowers workspace)`); + } + if (relativePath2 === "CLAUDE.md" || relativePath2 === "CHANGELOG.md" || relativePath2 === "README.md" || isRootMarkdown(relativePath2)) { + return allowed(`${relativePath2} (whitelist: root markdown)`); + } + let governing; + try { + governing = await governingChange(relativePath2, projectRoot, selectedChangeName); + } catch (error) { + return result( + 2, + `[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}` + ); + } + if (!governing) return allowed("no active comet change"); + if ("blockedResult" in governing) return governing.blockedResult; + if (governing.archived) return allowed(`${relativePath2} (own change archived)`); + const phase = governing.phase; + const openSpec = openSpecAllowed(relativePath2, phase); + if (openSpec) return allowed(openSpec); + if (isSuperpowersArtifactPath(relativePath2)) { + if (governing.superpowersArtifact === "matched" && allowsSuperpowersArtifacts(governing)) { + return allowed(`${relativePath2} (phase: ${phase}, superpowers)`); + } + if (governing.superpowersArtifact === "unmatched") { + return blockedUnmatchedSuperpowersArtifact(relativePath2, governing); + } + } + if (phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc) { + return blockedMissingDesignDoc(relativePath2); + } + if (phase === "build" || phase === "verify") { + return allowed(`${relativePath2} (phase: ${phase})`); + } + return blocked(relativePath2, phase); +} +async function inspectClassicHookGuard(projectRoot, changeName, request) { + const active = await activeChanges(projectRoot); + const selected = active.find((change) => governingChangeName(change) === changeName); + if (!selected) { + return { + allowed: false, + reason: `Selected Classic change ${changeName} is missing or archived; resume /comet-classic before retrying`, + workflow: "classic", + change: changeName + }; + } + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + if (selected.phase === "verify" || selected.phase === "build" && !(selected.classic?.workflow === "full" && !selected.classic.designDoc)) { + return { + allowed: true, + reason: `Classic change is in ${selected.phase}`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + return { + allowed: false, + reason: `Hook write target could not be determined while Classic change ${changeName} is in ${selected.phase}; resume /comet-classic before retrying`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + for (const target of request.targets) { + const inspected = await inspectClassicHookTarget(projectRoot, target, changeName); + if (inspected.exitCode !== 0) { + return { + allowed: false, + reason: inspected.stderr?.trim() || "Classic phase guard blocked the write", + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + } + return { + allowed: true, + reason: `Classic write allowed in ${selected.phase}`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; +} + +// domains/comet-native/native-hook-guard.ts +import { promises as fs12 } from "fs"; +import path12 from "path"; + +// domains/comet-native/native-change.ts +var import_yaml5 = __toESM(require_dist(), 1); +import { promises as fs11 } from "fs"; +import path11 from "path"; + +// domains/comet-native/native-bounded-file.ts +import { createHash } from "node:crypto"; +import { promises as fs8 } from "node:fs"; +import path8 from "node:path"; +import { TextDecoder } from "node:util"; + +// domains/comet-native/native-sensitive-paths.ts +var NATIVE_EXCLUDED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([ + ".cache", + ".git", + ".gradle", + ".gnupg", + ".mypy_cache", + ".next", + ".npm", + ".pnpm-store", + ".pytest_cache", + ".ssh", + ".turbo", + ".venv", + ".yarn", + "__pycache__", + "node_modules", + "venv" +]); +var NATIVE_SENSITIVE_FILE_NAMES = /* @__PURE__ */ new Set([ + ".git-credentials", + ".netrc", + ".npmrc", + ".pypirc", + "auth.json", + "credentials.json" +]); +function isNativeEnvFileName(name) { + return name.toLowerCase().startsWith(".env"); +} +function nativeSensitiveRelativePathReason(relativeRef) { + const segments = relativeRef.replaceAll("\\", "/").split("/").filter(Boolean); + const lower = segments.map((segment) => segment.toLowerCase()); + if (lower.some((segment) => isNativeEnvFileName(segment))) return "environment-file"; + if (lower.some((segment) => NATIVE_SENSITIVE_FILE_NAMES.has(segment))) { + return "credential-config"; + } + if (lower.includes(".git")) return "git-metadata"; + if (lower.some((segment) => NATIVE_EXCLUDED_DIRECTORY_NAMES.has(segment))) { + return "dependency-or-cache"; + } + if (lower.join("/") === ".comet/config.yaml") { + return "comet-config"; + } + if (lower.join("/") === ".comet/current-change.json") { + return "comet-selection"; + } + return null; +} + +// domains/comet-native/native-bounded-file.ts +var DEFAULT_NATIVE_ARTIFACT_MAX_BYTES = 1024 * 1024; +function isInside(parent, target) { + const relative = path8.relative(parent, target); + return relative === "" || !path8.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path8.sep}`); +} +function portableArtifactRef(value) { + const normalized2 = path8.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path8.posix.isAbsolute(normalized2) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized2 !== value || normalized2 === "." || value.endsWith("/")) { + throw new Error(`Native artifact ref must be normalized and relative: ${value}`); + } + const lower = value.toLowerCase(); + const sensitiveReason = nativeSensitiveRelativePathReason(value); + if (sensitiveReason || lower === "runtime" || lower.startsWith("runtime/")) { + throw new Error( + `Native artifact ref is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${value}` + ); + } + return value; +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native artifact byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function directoryIdentity(directory) { + const stat = await fs8.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native artifact parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs8.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory) { + const lexicalRoot = path8.resolve(root); + const lexicalDirectory = path8.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error("Native artifact path is outside its root"); + } + const chain = [await directoryIdentity(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path8.relative(lexicalRoot, lexicalDirectory).split(path8.sep).filter(Boolean)) { + cursor = path8.join(cursor, segment); + const identity = await directoryIdentity(cursor); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`Native artifact parent resolves outside its root: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain(chain) { + for (const identity of chain) { + const stat = await fs8.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs8.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native artifact parent changed while reading: ${identity.path}`); + } + } +} +async function readNativeBoundedTextFile(options) { + const ref = portableArtifactRef(options.ref); + const maxBytes = positiveLimit(options.maxBytes ?? DEFAULT_NATIVE_ARTIFACT_MAX_BYTES); + const file = path8.resolve(options.root, ...ref.split("/")); + const chain = await captureDirectoryChain(options.root, path8.dirname(file)); + await options.hooks?.afterParentChainCaptured?.(); + const before = await fs8.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native artifact must be a regular file: ${ref}`); + } + if (before.size > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + const realPath = await fs8.realpath(file); + if (!isInside(chain[0].realPath, realPath)) { + throw new Error(`Native artifact resolves outside its root: ${ref}`); + } + const handle = await fs8.open(file, "r"); + try { + const [opened, afterOpenPath, afterOpenRealPath] = await Promise.all([ + handle.stat(), + fs8.lstat(file), + fs8.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!opened.isFile() || !afterOpenPath.isFile() || afterOpenPath.isSymbolicLink() || afterOpenRealPath !== realPath || !sameFileIdentity(before, opened) || !sameFileIdentity(opened, afterOpenPath)) { + throw new Error(`Native artifact changed while opening: ${ref}`); + } + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (result2.bytesRead === 0) break; + total += result2.bytesRead; + if (total > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + chunks.push(Buffer.from(buffer.subarray(0, result2.bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs8.lstat(file), + fs8.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity(opened, afterHandle) || !sameFileIdentity(opened, afterPath)) { + throw new Error(`Native artifact changed while reading: ${ref}`); + } + const bytes = Buffer.concat(chunks, total); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`Native artifact is not valid UTF-8: ${ref}`, { cause: error }); + } + return { + ref, + size: total, + hash: createHash("sha256").update(bytes).digest("hex"), + text + }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-config.ts +var import_yaml4 = __toESM(require_dist(), 1); +import { promises as fs10 } from "fs"; +import path10 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml3 = __toESM(require_dist(), 1); + +// domains/comet-native/native-protected-file.ts +import { createHash as createHash2 } from "node:crypto"; +import { constants as fsConstants2, promises as fs9 } from "node:fs"; +import path9 from "node:path"; +import { TextDecoder as TextDecoder2 } from "node:util"; +function isInside2(parent, target) { + const relative = path9.relative(parent, target); + return relative === "" || !path9.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path9.sep}`); +} +function positiveLimit2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity(directory, label) { + const stat = await fs9.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs9.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain2(root, directory, label) { + const lexicalRoot = path9.resolve(root); + const lexicalDirectory = path9.resolve(directory); + if (!isInside2(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path9.relative(lexicalRoot, lexicalDirectory).split(path9.sep).filter(Boolean)) { + await verifyDirectoryChain2(chain, label); + cursor = path9.join(cursor, segment); + const identity = await captureDirectoryIdentity(cursor, label); + if (!isInside2(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain2(chain, label); + return chain; +} +async function verifyDirectoryChain2(chain, label) { + for (const identity of chain) { + const stat = await fs9.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity2(identity, stat) || await fs9.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit2(options.maxBytes); + const file = path9.resolve(options.file); + const chain = await captureDirectoryChain2(options.root, path9.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity(path9.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain2(chain, options.label); + const before = await fs9.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs9.realpath(file); + if (!isInside2(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside2(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants2.O_RDONLY : fsConstants2.O_RDONLY | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK; + const handle = await fs9.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs9.lstat(file), + fs9.realpath(file) + ]); + await verifyDirectoryChain2(chain, options.label); + await verifyDirectoryChain2(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity2(beforeIdentity, opened) || !sameFileIdentity2(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs9.lstat(file), + fs9.realpath(file) + ]); + await verifyDirectoryChain2(chain, options.label); + await verifyDirectoryChain2(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity2(beforeIdentity, afterHandle) || !sameFileIdentity2(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash2("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder2("utf-8", { fatal: true }).decode(snapshot.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot, text }; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot = record(value, "native.snapshot"); + rejectUnknown(snapshot, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path10.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs10.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml4.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} + +// domains/comet-native/native-lock.ts +import { AsyncLocalStorage } from "async_hooks"; +var NATIVE_LOCK_MAX_BYTES = 16 * 1024; +var nativeLockCoordinator = new AsyncLocalStorage(); + +// domains/comet-native/native-transaction.ts +import { TextDecoder as TextDecoder3 } from "util"; +var NATIVE_TRANSACTION_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_TRANSACTION_EVENTS_MAX_BYTES = 1024 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_BYTES = 16 * 1024; +var NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES = 64 * 1024 * 1024; +var UTF8_DECODER = new TextDecoder3("utf-8", { fatal: true }); + +// domains/comet-native/native-snapshot.ts +var DEFAULT_NATIVE_SNAPSHOT_LIMITS = { + maxFiles: 1e4, + maxFileBytes: 5 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxManifestBytes: 1024 * 1024 +}; +var NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES = 8 * 1024 * 1024; +var GIT_LIST_STDERR_LIMIT = 64 * 1024; +var GIT_TEXT_STDOUT_LIMIT = 64 * 1024; +var DEFAULT_NATIVE_GIT_SELECTION_LIMITS = { + maxRecords: 2e4, + maxBytes: 8 * 1024 * 1024, + maxRecordBytes: 64 * 1024 +}; +var DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS = { + maxNodes: 2e4, + maxBytes: 8 * 1024 * 1024, + maxPathBytes: 64 * 1024 +}; +var PHYSICAL_SELECTION_SUM_MASK = (1n << 256n) - 1n; + +// domains/engine/storage-run.ts +init_state(); + +// domains/comet-native/native-run-store.ts +var NATIVE_RUN_IO_LIMITS = { + runStateBytes: 256 * 1024, + trajectoryBytes: 8 * 1024 * 1024, + trajectoryEvents: 4096, + trajectoryEventBytes: 256 * 1024, + checkpointBytes: 256 * 1024, + pendingActionBytes: 256 * 1024, + contextBytes: 1024 * 1024, + artifactsBytes: 1024 * 1024 +}; + +// domains/comet-native/native-workspace.ts +var MAX_WORKSPACE_IDENTITY_BYTES = 16 * 1024; + +// domains/comet-native/native-types.ts +var NATIVE_RUNTIME_PROTOCOL_VERSION = 3; +var NATIVE_CHANGE_SCHEMA = "comet.native.v3"; +var NATIVE_V2_CHANGE_SCHEMA = "comet.native.v2"; +var NATIVE_LEGACY_CHANGE_SCHEMA = "comet.native.v1"; + +// domains/comet-native/native-change.ts +var CHANGE_KEYS = [ + "schema", + "name", + "language", + "phase", + "brief", + "approval", + "spec_changes", + "verification_result", + "verification_report", + "archived", + "created_at", + "run_id" +]; +var LEGACY_CHANGE_KEYS = new Set(CHANGE_KEYS); +var V2_CHANGE_KEYS = /* @__PURE__ */ new Set([...CHANGE_KEYS, "minimum_runtime_version", "revision"]); +var CURRENT_CHANGE_KEYS = /* @__PURE__ */ new Set([ + ...V2_CHANGE_KEYS, + "approved_contract_hash", + "implementation_scope", + "verification_evidence", + "partial_allowance" +]); +var SPEC_CHANGE_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "source", "base_hash"]); +var PHASES2 = /* @__PURE__ */ new Set(["shape", "build", "verify", "archive"]); +var APPROVALS = /* @__PURE__ */ new Set(["implicit", "confirmed"]); +var VERIFY_RESULTS2 = /* @__PURE__ */ new Set(["pending", "pass", "fail"]); +var NATIVE_CHANGE_STATE_FILE = "comet-state.yaml"; +var HASH_PATTERN = /^[a-f0-9]{64}$/u; +var NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CONTENT_ADDRESSED_REF_PATTERN = /^runtime\/evidence\/(scopes|allowances|verifications)\/([a-f0-9]{64})\.json$/u; +var NativeSchemaMigrationRequiredError = class extends Error { + constructor(change, schema) { + super( + `Native change ${change} uses ${schema}; run comet native doctor ${change} --repair before mutating it` + ); + this.change = change; + this.schema = schema; + this.name = "NativeSchemaMigrationRequiredError"; + } + change; + schema; + code = "native-schema-migration-required"; +}; +var NativeRuntimeCompatibilityError = class extends Error { + constructor(schema, minimumRuntimeVersion) { + super( + schema !== NATIVE_CHANGE_SCHEMA || minimumRuntimeVersion === null ? `Unsupported Native change schema ${schema} for runtime protocol ${NATIVE_RUNTIME_PROTOCOL_VERSION}` : `Native change ${schema} requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + this.schema = schema; + this.minimumRuntimeVersion = minimumRuntimeVersion; + this.name = "NativeRuntimeCompatibilityError"; + } + schema; + minimumRuntimeVersion; + code = "native-runtime-incompatible"; +}; +var NATIVE_BRIEF_TEMPLATE = [ + "# Outcome", + "", + "# Scope", + "", + "# Non-goals", + "", + "# Acceptance examples", + "", + "# Constraints and invariants", + "", + "# Decisions", + "", + "# Open questions", + "", + "# Verification expectations", + "" +].join("\n"); +function record2(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown2(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function assertNativeName(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native change name: ${value}`); +} +function assertCapabilityId(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native capability id: ${value}`); +} +function assertRelativeRef(value, label) { + if (value.length === 0 || path11.isAbsolute(value) || /^(?:[A-Za-z]:|~|[\\/])/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native change`); + } +} +function parseSpecChange(value, index) { + const item = record2(value, `spec_changes[${index}]`); + rejectUnknown2(item, SPEC_CHANGE_KEYS, `spec_changes[${index}]`); + if (typeof item.capability !== "string") throw new Error("spec change capability is required"); + assertCapabilityId(item.capability); + if (item.operation !== "create" && item.operation !== "replace" && item.operation !== "remove") { + throw new Error(`Invalid spec operation for ${item.capability}`); + } + const source = item.source; + const baseHash = item.base_hash; + if (source !== void 0 && typeof source !== "string") { + throw new Error(`Spec source for ${item.capability} must be a string`); + } + if (typeof source === "string") assertRelativeRef(source, `Spec source for ${item.capability}`); + if (item.operation === "create") { + if (!source) throw new Error(`Create spec ${item.capability} requires source`); + if (baseHash !== null) + throw new Error(`Create spec ${item.capability} requires null base_hash`); + } else if (item.operation === "replace") { + if (!source) throw new Error(`Replace spec ${item.capability} requires source`); + if (typeof baseHash !== "string" || !HASH_PATTERN.test(baseHash)) { + throw new Error(`Replace spec ${item.capability} requires a SHA-256 base_hash`); + } + } else { + if (source !== void 0) throw new Error(`Remove spec ${item.capability} forbids source`); + if (typeof baseHash !== "string" || !HASH_PATTERN.test(baseHash)) { + throw new Error(`Remove spec ${item.capability} requires a SHA-256 base_hash`); + } + } + return { + capability: item.capability, + operation: item.operation, + ...typeof source === "string" ? { source } : {}, + base_hash: baseHash + }; +} +function validDate(value) { + if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) return false; + return (/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`)).toISOString().slice(0, 10) === value; +} +function parseChangeFields(root, knownKeys) { + rejectUnknown2(root, knownKeys, NATIVE_CHANGE_STATE_FILE); + if (typeof root.name !== "string") throw new Error("Native change name is required"); + assertNativeName(root.name); + if (root.language !== "en" && root.language !== "zh-CN") { + throw new Error("Native change language must be en or zh-CN"); + } + if (typeof root.phase !== "string" || !PHASES2.has(root.phase)) { + throw new Error("Native change phase is invalid"); + } + if (root.brief !== "brief.md") throw new Error("Native change brief must be brief.md"); + if (root.approval !== null && !APPROVALS.has(root.approval)) { + throw new Error("Native change approval is invalid"); + } + if (!Array.isArray(root.spec_changes)) throw new Error("Native spec_changes must be an array"); + const specChanges = root.spec_changes.map(parseSpecChange); + const duplicates = specChanges.map((change) => change.capability).filter((capability, index, all) => all.indexOf(capability) !== index); + if (duplicates.length > 0) { + throw new Error( + `Duplicate Native capability operation: ${[...new Set(duplicates)].join(", ")}` + ); + } + if (typeof root.verification_result !== "string" || !VERIFY_RESULTS2.has(root.verification_result)) { + throw new Error("Native verification_result is invalid"); + } + if (root.verification_report !== null && typeof root.verification_report !== "string") { + throw new Error("Native verification_report must be a string or null"); + } + if (typeof root.verification_report === "string") { + assertRelativeRef(root.verification_report, "Native verification_report"); + } + if (typeof root.archived !== "boolean") throw new Error("Native archived must be boolean"); + if (typeof root.created_at !== "string" || !validDate(root.created_at)) { + throw new Error("Native created_at must be a valid YYYY-MM-DD date"); + } + if (root.run_id !== null && (typeof root.run_id !== "string" || root.run_id.length === 0)) { + throw new Error("Native run_id must be a non-empty string or null"); + } + return { + name: root.name, + language: root.language, + phase: root.phase, + brief: "brief.md", + approval: root.approval, + spec_changes: specChanges, + verification_result: root.verification_result, + verification_report: root.verification_report, + archived: root.archived, + created_at: root.created_at, + run_id: root.run_id + }; +} +function parseLegacyNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_LEGACY_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_LEGACY_CHANGE_SCHEMA}`); + } + return { + schema: NATIVE_LEGACY_CHANGE_SCHEMA, + ...parseChangeFields(root, LEGACY_CHANGE_KEYS) + }; +} +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function contentAddressedRef(value, label, kind) { + if (value === null) return null; + const match = typeof value === "string" ? CONTENT_ADDRESSED_REF_PATTERN.exec(value) : null; + if (!match || match[1] !== kind) { + throw new Error( + `${label} must be null or runtime/evidence/${kind}/.json relative to the Native change` + ); + } + return value; +} +function approvedContractHash(value) { + if (value === void 0 || value === null) return null; + if (typeof value !== "string" || !HASH_PATTERN.test(value)) { + throw new Error("Native approved_contract_hash must be null or a SHA-256 hash"); + } + return value; +} +function parseV2NativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_V2_CHANGE_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native v2 minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error(`Native ${NATIVE_V2_CHANGE_SCHEMA} minimum_runtime_version must be 2`); + } + return { + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision: positiveInteger(root.revision, "Native v2 revision"), + ...parseChangeFields(root, V2_CHANGE_KEYS) + }; +} +function parseNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA || root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const previous = root.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? parseLegacyNativeChangeValue(root) : parseV2NativeChangeValue(root); + throw new NativeSchemaMigrationRequiredError(previous.name, previous.schema); + } + throw new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + typeof root.minimum_runtime_version === "number" ? root.minimum_runtime_version : null + ); + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native ${root.schema} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger(root.revision, "Native revision"); + const fields = parseChangeFields(root, CURRENT_CHANGE_KEYS); + const approvalHash = approvedContractHash(root.approved_contract_hash); + if (fields.approval === null && approvalHash !== null) { + throw new Error("Native approved_contract_hash requires an approval"); + } + return { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + ...fields, + approved_contract_hash: approvalHash, + implementation_scope: contentAddressedRef( + root.implementation_scope, + "Native implementation_scope", + "scopes" + ), + verification_evidence: contentAddressedRef( + root.verification_evidence, + "Native verification_evidence", + "verifications" + ), + partial_allowance: contentAddressedRef( + root.partial_allowance, + "Native partial_allowance", + "allowances" + ) + }; +} +function inspectNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + const state2 = parseLegacyNativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: 1, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const state2 = parseV2NativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: state2.minimum_runtime_version, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + const minimumRuntimeVersion2 = typeof root.minimum_runtime_version === "number" && Number.isSafeInteger(root.minimum_runtime_version) ? root.minimum_runtime_version : null; + return { + status: "runtime-incompatible", + schema: typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion: minimumRuntimeVersion2, + state: null, + message: new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion2 + ).message + }; + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + return { + status: "runtime-incompatible", + schema: root.schema, + minimumRuntimeVersion, + state: null, + message: new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion).message + }; + } + const state = parseNativeChangeValue(root); + return { + status: "current", + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + state + }; +} +function nativeChangeDir(paths, name) { + assertNativeName(name); + const target = path11.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, target)) throw new Error("Native change path escaped"); + return target; +} +async function hasPendingNativeSchemaMigration(paths, name) { + const file = path11.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs11.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +var NATIVE_CHANGE_DOCUMENT_MAX_BYTES = 256 * 1024; +async function readChangeDocumentFile(file, root = path11.dirname(file)) { + const ref = path11.relative(root, file).split(path11.sep).join("/"); + const source = await readNativeBoundedTextFile({ + root, + ref, + maxBytes: NATIVE_CHANGE_DOCUMENT_MAX_BYTES + }); + const document = (0, import_yaml5.parseDocument)(source.text, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid Native change file ${file}: ${document.errors[0].message}`); + } + return document.toJS(); +} +async function inspectNativeChange(paths, name) { + const file = path11.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + const inspection = inspectNativeChangeValue(await readChangeDocumentFile(file, paths.nativeRoot)); + if (inspection.state && inspection.state.name !== name) { + throw new Error(`Native change directory/name mismatch: ${name}`); + } + if (await hasPendingNativeSchemaMigration(paths, name)) { + return { + status: "migration-required", + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + state: inspection.state, + message: `Native schema migration is incomplete for ${name}; run doctor --repair` + }; + } + return inspection; +} +async function readNativeChange(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + throw new NativeSchemaMigrationRequiredError(name, inspection.schema); + } + if (inspection.status === "runtime-incompatible" || !inspection.state) { + throw new NativeRuntimeCompatibilityError(inspection.schema, inspection.minimumRuntimeVersion); + } + return inspection.state; +} + +// domains/comet-native/native-selection.ts +var NATIVE_SELECTION_MAX_BYTES = 16 * 1024; +async function readNativeSelectionRecord(paths) { + const current = await readCometCurrentSelection(paths.projectRoot); + if (current.status === "missing" || current.selection.workflow !== "native") return null; + assertNativeName(current.selection.change); + return current.selection; +} +async function resolveSelectedNativeChange(paths) { + const value = await readNativeSelectionRecord(paths); + if (!value) return null; + await readNativeChange(paths, value.change); + return value.change; +} + +// domains/comet-native/native-hook-guard.ts +function isWithin(parent, target) { + const relative = path12.relative(parent, target); + return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative); +} +function requestTargetsAreControlOnly(projectRoot, nativeRoot, request) { + return request.targets.length > 0 && request.targets.every((targetPath) => { + const target = path12.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) return true; + const relative = path12.relative(projectRoot, target).replaceAll("\\", "/"); + return relative === ".comet/config.yaml" || isWithin(nativeRoot, target); + }); +} +async function activeNativeContext(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (!config || !(config.workflows ?? [config.default_workflow]).includes("native")) return null; + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + let entries; + try { + entries = await fs12.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return { paths, changes: [] }; + throw error; + } + const changes = []; + for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry.isDirectory() || entry.isSymbolicLink()) continue; + const state = await readNativeChange(paths, entry.name); + if (!state.archived) changes.push(state); + } + return { paths, changes }; +} +async function listActiveNativeHookChanges(projectRoot) { + const context = await activeNativeContext(projectRoot); + return (context?.changes ?? []).map((change) => ({ + workflow: "native", + name: change.name, + phase: change.phase + })); +} +async function inspectNativeHookGuard(projectRoot, request, selectedChangeName) { + const context = await activeNativeContext(projectRoot); + if (!context) return { allowed: true, reason: "Native workflow is not enabled" }; + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (context.changes.length === 0) { + return { + allowed: true, + reason: requestTargetsAreControlOnly(projectRoot, context.paths.nativeRoot, request) ? "Native control artifact write" : "No Native changes exist" + }; + } + let change; + if (selectedChangeName) { + change = context.changes.find((candidate) => candidate.name === selectedChangeName); + if (!change) { + return { + allowed: false, + reason: `Selected Native change ${selectedChangeName} is missing or archived; resume /comet-native before retrying`, + workflow: "native", + change: selectedChangeName + }; + } + } else if (context.changes.length === 1) { + change = context.changes[0]; + } else { + const selectedName = await resolveSelectedNativeChange(context.paths); + change = context.changes.find((candidate) => candidate.name === selectedName); + if (!change) { + return { + allowed: false, + reason: "Multiple Native changes are active; select the change to resume before writing code", + workflow: "native" + }; + } + } + if (change.phase === "build") { + return { + allowed: true, + reason: "Native change is in Build", + workflow: "native", + phase: change.phase, + change: change.name + }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + return { + allowed: false, + reason: `Hook write target could not be determined while Native change ${change.name} is in ${change.phase}; resume /comet-native before retrying`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + let controlTarget = false; + let externalTarget = false; + for (const targetPath of request.targets) { + const target = path12.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) { + externalTarget = true; + continue; + } + const relative = path12.relative(projectRoot, target).replaceAll("\\", "/"); + if (relative === ".comet/config.yaml" || isWithin(context.paths.nativeRoot, target)) { + controlTarget = true; + continue; + } + return { + allowed: false, + reason: `Native change ${change.name} is in ${change.phase}; implementation writes are only allowed in build. Resume /comet-native to continue safely`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + return { + allowed: true, + reason: controlTarget ? "Native control artifact write" : externalTarget ? "Write target is outside the guarded project" : "No guarded write target was provided", + workflow: "native", + phase: change.phase, + change: change.name + }; +} + +// domains/comet-entry/hook-router.ts +var DEFAULT_DEPENDENCIES = { + listNative: listActiveNativeHookChanges, + listClassic: listActiveClassicHookChanges, + inspectNative: inspectNativeHookGuard, + inspectClassic: inspectClassicHookGuard +}; +function enabledWorkflows(config) { + if (!config) return ["classic"]; + return config.workflows ?? [config.default_workflow]; +} +async function resolveHookWorkflowOwner(projectRoot, dependencies = DEFAULT_DEPENDENCIES) { + const config = await readProjectConfig(projectRoot); + const enabled = enabledWorkflows(config); + let current; + try { + current = await readCometCurrentSelection(projectRoot); + } catch (error) { + return { + status: "stale", + code: "selection-unreadable", + reason: error instanceof Error ? error.message : String(error) + }; + } + if (current.status === "selected") { + const selection = current.selection; + if (!enabled.includes(selection.workflow)) { + return { + status: "stale", + code: "workflow-disabled", + reason: `selected workflow '${selection.workflow}' is not enabled for this project` + }; + } + let selectedCandidates; + try { + selectedCandidates = selection.workflow === "native" ? await dependencies.listNative(projectRoot) : await dependencies.listClassic(projectRoot); + } catch (error) { + return { + status: "stale", + code: "change-state-unreadable", + reason: `cannot safely enumerate active Comet changes: ${error instanceof Error ? error.message : String(error)}` + }; + } + const owner = selectedCandidates.find((candidate) => candidate.name === selection.change); + if (!owner) { + return { + status: "stale", + code: "target-missing", + reason: `selected ${selection.workflow} change '${selection.change}' is missing or archived` + }; + } + if (selection.workflow === "classic") { + const resolved = await resolveCurrentChange(projectRoot); + if (resolved.status !== "selected") { + return { + status: "stale", + code: "classic-selection-invalid", + reason: resolved.status === "stale" ? resolved.reason : `selected Classic change '${selection.change}' is no longer active` + }; + } + } + return { status: "owned", owner }; + } + let native; + let classic; + try { + [native, classic] = await Promise.all([ + enabled.includes("native") ? dependencies.listNative(projectRoot) : Promise.resolve([]), + enabled.includes("classic") ? dependencies.listClassic(projectRoot) : Promise.resolve([]) + ]); + } catch (error) { + return { + status: "stale", + code: "change-state-unreadable", + reason: `cannot safely enumerate active Comet changes: ${error instanceof Error ? error.message : String(error)}` + }; + } + const candidates = [...native, ...classic]; + if (candidates.length === 0) return { status: "none" }; + if (candidates.length === 1) return { status: "inferred", owner: candidates[0] }; + return { status: "ambiguous", candidates }; +} +async function inspectCometHook(projectRoot, request, dependencies = DEFAULT_DEPENDENCIES) { + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + try { + const resolution = await resolveHookWorkflowOwner(projectRoot, dependencies); + if (resolution.status === "none") { + return { allowed: true, reason: "No active Comet change" }; + } + if (resolution.status === "stale") { + return { + allowed: false, + reason: `${resolution.reason}. Resume /comet-native or /comet-classic and select the current change before retrying` + }; + } + if (resolution.status === "ambiguous") { + return { + allowed: false, + reason: `Multiple active Comet changes require one current selection: ${resolution.candidates.map((candidate) => `${candidate.workflow}:${candidate.name}`).join(", ")}` + }; + } + const owner = resolution.owner; + return owner.workflow === "native" ? dependencies.inspectNative(projectRoot, request, owner.name) : dependencies.inspectClassic(projectRoot, owner.name, request); + } catch (error) { + return { + allowed: false, + reason: `Comet Hook Router failed closed: ${error instanceof Error ? error.message : String(error)}` + }; + } +} + +// domains/comet-entry/hook-router-entry.ts +var USAGE = "Usage: comet-hook-router --platform [--project-root ]"; +function parseArgs(args) { + let platformId; + let projectRoot; + for (let index = 0; index < args.length; index++) { + const arg = args[index]; + if (arg === "--platform") { + platformId = args[++index]; + continue; + } + if (arg === "--project-root") { + projectRoot = args[++index]; + continue; + } + throw new Error(`Unknown argument: ${arg}`); + } + if (!platformId || platformId.startsWith("--")) throw new Error("--platform is required"); + if (!COMET_HOOK_PLATFORM_IDS.has(platformId)) { + throw new Error(`unsupported Hook platform: ${platformId}`); + } + if (projectRoot?.startsWith("--")) throw new Error("--project-root requires a value"); + return { platformId, ...projectRoot ? { projectRoot: path13.resolve(projectRoot) } : {} }; +} +async function projectRootFrom(parsed) { + if (parsed.projectRoot) return parsed.projectRoot; + const discovered = await discoverNativeProject(process.cwd()); + for (const marker of [[".comet", "config.yaml"], [".git"], ["openspec", "changes"]]) { + try { + await fs13.lstat(path13.join(discovered, ...marker)); + return discovered; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + } + let cursor = path13.resolve(process.cwd()); + while (true) { + try { + await fs13.lstat(path13.join(cursor, "openspec", "changes")); + return cursor; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const parent = path13.dirname(cursor); + if (parent === cursor) break; + cursor = parent; + } + return null; +} +async function runCometHookRouter(args) { + let parsed; + try { + parsed = parseArgs(args); + } catch (error) { + process.stderr.write(`${error instanceof Error ? error.message : String(error)} +${USAGE} +`); + return 64; + } + let decision; + try { + const projectRoot = await projectRootFrom(parsed); + decision = projectRoot ? await inspectCometHook(projectRoot, readCometHookRequest()) : { allowed: true, reason: "No Comet project discovered" }; + } catch (error) { + decision = { + allowed: false, + reason: `Comet Hook Router failed closed during project discovery: ${error instanceof Error ? error.message : String(error)}` + }; + } + const output = renderCometHookDecision(parsed.platformId, decision); + if (output.stdout) process.stdout.write(output.stdout); + if (output.stderr) process.stderr.write(output.stderr); + return output.exitCode; +} +process.exitCode = await runCometHookRouter(process.argv.slice(2)); +export { + projectRootFrom, + runCometHookRouter +}; diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 0000000..c6cca16 --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "node \"D:/My Project/pi-tool-display/.agents/skills/comet/scripts/comet-hook-router.mjs\" --platform \"codex\" --project-root \"D:/My Project/pi-tool-display\"" + } + ] + } + ] + } +} diff --git a/.codex/rules/comet-workflow-guard.md b/.codex/rules/comet-workflow-guard.md new file mode 100644 index 0000000..3e2a64b --- /dev/null +++ b/.codex/rules/comet-workflow-guard.md @@ -0,0 +1,32 @@ +# Comet 当前需求阶段规则 + +本规则是 Native 与 Classic 共用的常驻软性防线。项目可以启用两种 workflow,但一个需求只能由一个 workflow/change 管理;不得同时套用两套阶段规则。 + +## 先确定当前需求 + +每轮开始、恢复工作或怀疑上下文被压缩后,按以下顺序检查: + +1. 读取 `.comet/config.yaml`:`workflows` 表示项目启用的能力,`default_workflow` 只决定 `/comet` 的默认入口。 +2. 读取 `.comet/current-change.json`:其中的 `workflow + change` 才是当前需求所有者。 +3. selection 缺失时,只有全项目恰好一个 Comet 活跃 change 才能只读推断;存在多个候选时必须暂停并让用户选择。 +4. selection 无效、过期、跨分支失效或指向已归档 change 时停止,不得回退到 `default_workflow` 猜测。 + +Classic 旧项目没有新版配置时只按 Classic legacy fallback 处理,不得因此启用 Native。 + +## 只应用选中的阶段规则 + +| Workflow | 禁止普通实现写入 | 允许普通实现写入 | +| --- | --- | --- | +| Native | Shape、Verify、Archive | Build | +| Classic | Open、Design、Archive | Build、Verify | + +- Native 的 Verify 只运行检查并记录证据;发现实现问题时,先记录失败并通过 Native Runtime 回到 Build,再修改实现。点号开头的普通项目文件不因名称而自动成为跨阶段白名单。 +- 当前 workflow 是 Native:恢复 `/comet-native`,由 Native 状态、证据和自动推进协议继续。 +- 当前 workflow 是 Classic:恢复 `/comet-classic`,由 Classic 状态、确认点和阶段协议继续。 +- 不要把 Native change 转换成 Classic change,或反向转换;切换 workflow 必须选择另一个独立 change。 + +## Hook 约束 + +平台只应安装一个 Comet Hook Router。一次写入事件最多进入一个 workflow Guard;不得分别运行 Native 和 Classic Hook。 + +Hook 会对多文件和 patch 目标整体裁决。当前阶段不允许普通项目写入、selection 不明确、状态失效或目标无法安全判断时会失败关闭。不要绕过 Hook;按拒绝信息恢复对应 workflow 并重新选择当前 change。 diff --git a/.comet/config.yaml b/.comet/config.yaml new file mode 100644 index 0000000..c561d10 --- /dev/null +++ b/.comet/config.yaml @@ -0,0 +1,33 @@ +# Comet 使用的配置格式版本,请勿修改此值。 +schema: comet.project.v1 +# `/comet` 默认进入的工作流;该值也必须出现在 workflows 中。 +default_workflow: native +# 此项目启用的工作流,可填写 native、classic 或同时启用两者。 +workflows: + - native +# 是否启用只读的环境感知恢复探针,同时作用于 Native 和 Classic;设为 false 可关闭自动工作流恢复。 +# ambient_resume: true | false +ambient_resume: true +# Native 工作流配置,不会改变 Classic 的状态或行为。 +native: + # Native 产物的存放根目录,包括规格、change 和运行时数据。 + artifact_root: docs + # Native 工作流文档使用的产物语言。 + # 可选值:en | zh-CN + language: zh-CN + # Native 每轮询问一个问题,或一次提出当前所有可回答的问题。 + # 可选值:sequential | batch + clarification_mode: sequential + # Native 内容快照使用的可审计项目范围与有界工作预算。 + snapshot: + # Native 快照纳入的项目相对路径;模式使用 /,支持 *、** 和 ?。 + include: + - "**/*" + # 从纳入范围中排除路径;新 change 会把排除策略绑定到 baseline。 + exclude: [] + # 单次快照最多捕获的文件数;大型 monorepo 可按需提高。 + max_files: 10000 + # 单次快照最多哈希的文件内容总字节数;内容采用流式读取,不依赖 Git hash。 + max_total_bytes: 268435456 + # 单次快照的最长执行时间(毫秒);较慢或更大的仓库应与字节预算一并提高。 + max_duration_ms: 60000 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..a4365d4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,30 @@ +name: Bug report +description: Report a reproducible problem +title: "[Bug]: " +labels: [bug] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: What happened, and what did you expect? + validations: + required: true + - type: input + id: versions + attributes: + label: Versions + description: Include @plus/pi-tool-display, Pi, Node, OS, and terminal versions. + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Reproduction + description: Provide the smallest reliable reproduction. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant logs or screenshots diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..f79b9db --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## Summary + + + +## Scope + + + +## Verification + + + +## Closing References + + diff --git a/.github/workflows/full-verify.yml b/.github/workflows/full-verify.yml new file mode 100644 index 0000000..da1d4ae --- /dev/null +++ b/.github/workflows/full-verify.yml @@ -0,0 +1,22 @@ +name: Full Verify + +on: + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +jobs: + full-verify: + name: Full Verify + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: python .build-and-verify/runtime/build_and_verify.py verify --project . --full diff --git a/.gitignore b/.gitignore index c7459a7..d694c0b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,6 @@ node_modules/ dist/ coverage/ config.json +!.build-and-verify/config.json *.tmp .local/ diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 9fa6f73..0000000 --- a/.npmignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules -config.json -*.log -.git -.gitignore -.npmignore -tsconfig.json diff --git a/.pi/extensions/comet-commands.ts b/.pi/extensions/comet-commands.ts new file mode 100644 index 0000000..1233631 --- /dev/null +++ b/.pi/extensions/comet-commands.ts @@ -0,0 +1,18 @@ +import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; + +const commands = [ + "comet", + "comet-any", + "comet-native" +] as const; + +export default function registerCometCommands(pi: ExtensionAPI) { + for (const name of commands) { + pi.registerCommand(name, { + description: `Comet: /${name}`, + handler: async (args) => { + pi.sendUserMessage(args ? `/skill:${name} ${args}` : `/skill:${name}`); + }, + }); + } +} diff --git a/.pi/settings.json b/.pi/settings.json new file mode 100644 index 0000000..a08f8f2 --- /dev/null +++ b/.pi/settings.json @@ -0,0 +1,8 @@ +{ + "enableSkillCommands": true, + "skills": [ + "-skills/comet/SKILL.md", + "-skills/comet-any/SKILL.md", + "-skills/comet-native/SKILL.md" + ] +} diff --git a/.pi/skills/comet-any/SKILL.md b/.pi/skills/comet-any/SKILL.md new file mode 100644 index 0000000..2e9bc3f --- /dev/null +++ b/.pi/skills/comet-any/SKILL.md @@ -0,0 +1,118 @@ +--- +name: comet-any +description: "仅在用户明确调用 /comet-any,或明确要求定制 /comet-classic 五阶段流程、创建或升级由 Comet Creator 管理的 workflow Skill 时使用;不要用于一般 Skill 的编写、整理或评审。" +--- + +# Comet Any - Skill Creator + +`/comet-any` 是 Comet 的 Skill 创建向导。用户只需要描述想要的工作流;本 Skill 负责读取真实 Skill、提出方案、等待确认、生成可验证的 Comet-native Skill Bundle,并通过内部 CLI 完成 eval、review、publish readiness 和安装预览。 + +普通用户第一层只看到三种起点: + +- `基于 /comet-classic 的五阶段定制`:覆盖 `open / design / build / verify / archive` 五阶段的 Skill 编排,但不修改 `/comet-classic` 永久入口本身。 +- `创建全新 workflow Skill`:从目标和候选 Skills 生成新的 `workflow-kernel`。 +- `整理已有 Skill`:读取已有 Skill,补齐 Workflow Node、Skill Binding、Output Schema、Guardrail、Handoff、eval 和 readiness。 + +后端 Bundle、Factory、composition 仍是内部审计词;不要把它们作为普通用户的第一屏概念。 + +## 核心模型 + +所有路径都必须编译到同一种 Workflow Contract: + +- `Workflow Node`:流程中的可恢复节点,例如 `open`、`design`、`plan`、`execute`、`subagent-execute`、`review`、`verify`、`archive`。 +- `Node Responsibility`:该 Node 在 Agent workflow 中承担的职责,用来解释它为什么存在、需要产出什么、能否替换。 +- `Skill Binding`:某个 Node 的实现 Skill 或辅助 Skill。 +- `Required Skill Call`:要求 Node 内必须调用某个 Skill,不替换 Node implementation。例如 `execute` 和 `subagent-execute` 必须调用 `elementui`,`review` 必须调用 `whitebox-code-standard`。 +- `Output Schema`:Node 必须产出的文件、状态或 evidence。Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。脚本、eval、readiness 只依赖挂到 Node 的 Output Schema,不依赖 Skill 名称。 +- `Guardrail`:阻断或放行 Node 推进的检查。 +- `Handoff`:子代理或跨 Node 交接时必须带回的 evidence。 +- `workflow-protocol.json`:生成包的唯一运行事实源,kind 为 `comet-five-phase-overlay` 或 `workflow-kernel`。 + +## 受保护边界 + +`comet-five-phase-overlay` 保留 Comet Classic 五阶段主流程和 `.comet.yaml` 状态语义。普通模式下: + +- `comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`;没有 active change 或多个 active changes 时必须阻塞并请用户选择。 +- 不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态。Bundle 草稿、eval evidence 和 publish readiness 可以有自己的证据文件,但不能替代 `.comet.yaml`。 +- `control` Node 不允许 override:`open`、`execute`、`verify`、`archive`。 +- `producer` Node 可以 override:`design`、`plan`,但必须满足对应 Output Schema。 +- `handoff` 和 `guardrail` Node 可以 require / augment。 +- 用户坚持替换 control Node 时,改走高级 `workflow-kernel`,并要求重新声明 state、Output Schema 和 Guardrail。 +- 所有 Node 都必须用 responsibility 说明职责,不使用内部坐标作为用户理解流程的方式。 + +## 工作步骤 + +1. 恢复现有状态:先运行 `comet creator guide --project . --json`,展示恢复摘要和下一步。 +2. 读取项目偏好:读取 `.comet/skill-preferences.yaml`,用 `comet creator candidates --json` 发现真实本地 Skill,再用 `comet skill show --json` 读取候选的真实内容与 hash。不得只按名字推测能力。 +3. 生成方案:把用户目标表达为 Workflow Nodes、Skill Bindings、Output Schemas、Guardrails、Handoffs 和 Evidence。 +4. 展示确认页:说明每个 Node 的职责、绑定 Skill、Required Skill Call、Output Schema、可执行披露和 readiness 影响。确认页必须为每个新增 binding 或 schema 显示 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +5. 等待用户确认:未确认前不得写 Bundle draft;存在 missing / ambiguous Skill 时必须暂停。 +6. 初始化后端状态:确认后调用 `comet creator init --file --confirmed-proposal --json`。 +7. 运行创作管线并生成 Bundle:先运行 `comet creator authoring-plan --depth quick|full --json` 取得 lane DAG。按 DAG 派发 lane——wave1(`script`、`reference`、`pause-points`)在支持子代理的平台可并发(否则按依赖顺序内联),wave2(`workflow-entry`、`skill-core`)在 script 契约之后,`skill-review` 作为汇聚 barrier。每个 lane 的产出用 `comet creator authoring-record --lane --file --json` 记录(经 schema 校验;BLOCKED/NEEDS_CONTEXT 会被拒绝)。随后运行 `comet creator generate --json`:把记录的内容叶子草稿(entry/node SKILL.md、decision-points、recovery)合并进包,而确定性脊梁(protocol/scripts/manifest)保持模板化,并渲染真实审查证据。产出 entry Skill、Node Skills、`reference/workflow-protocol.json`、六个 scripts、rules、hooks 与 `comet/eval.yaml`。 +8. 验证:展示 quick/full eval 工作量,运行或记录当前 draft hash 的 eval evidence;失败、skip 或证据 hash 过期时不得进入 ready。 +9. Review / readiness:读取 `comet publish review --platform --json`,展示 `Readiness:`、`Blockers:`、`Warnings:`、`Evidence:`。 +10. Publish / install preview:人工批准后才能 publish;安装前必须先运行 preview,并展示 `No files were written`。 + +## 方案示例 + +组件库和白盒审查场景应生成类似 plan: + +```json +{ + "goal": "基于 /comet-classic 的五阶段定制,要求组件库和白盒审查。", + "skillCreatorIntent": "customize-comet", + "workflow": { + "kind": "comet-five-phase-overlay", + "name": "team-comet", + "goal": "要求组件库和白盒审查。", + "nodes": { + "execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "reason": "Use project component library during direct implementation." + } + ] + }, + "subagent-execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "scope": "handoff" + } + ] + }, + "review": { + "requiredSkillCalls": [ + { + "skill": "whitebox-code-standard", + "scope": "review" + } + ] + } + } + } +} +``` + +## 硬性规则 + +- 必须先展示方案确认页,再生成。 +- 确认页必须为每个新增 binding 或 schema 显示 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +- Required Skill Call 不替换 Node implementation。 +- producer override 必须声明 `satisfies` 的 Output Schema。 +- Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。 +- control Node 普通模式不得 override。 +- eval、review、publish readiness 必须读取同一份 `workflow-protocol.json`。 +- readiness blockers 必须阻止 publish:缺少当前 draft hash 的 eval evidence、人工 approval、required capability 或 executable disclosure 任一项都不能进入 ready。 +- 子代理 Handoff 必须要求子代理加载 Required Skill Call 并回传 evidence。 +- 脚本只读取 protocol 和 state,不把 Skill 名称当成校验依据。 +- 安装前必须询问用户,不得自动安装。 + +## 参考资料 + +- `comet-any/reference/authoring-protocol.json` +- `comet-any/reference/authored-zone-example.md` +- `comet-any/reference/bundle-authoring.md` +- `comet-any/reference/authoring-subagents.md` +- `comet-any/reference/eval-provider.md` diff --git a/.pi/skills/comet-any/reference/authored-zone-example.md b/.pi/skills/comet-any/reference/authored-zone-example.md new file mode 100644 index 0000000..0bd453b --- /dev/null +++ b/.pi/skills/comet-any/reference/authored-zone-example.md @@ -0,0 +1,105 @@ +# Authored 区质量标尺(样例) + +这是你编写的 **Authored 区**(entry 的 `## Decision Core`、node 的 `## Guidance`)的具体质量标尺。生成器把你的 Authored 区组装到确定性 **Auto 区**(frontmatter、路由表、Entry/Exit Check、证据格式、Recovery)之上。**你只写 Authored 区**。请按本样例校准,而不是写一行套话。 + +## "Auto" 与 "Authored" 的含义 + +- **Auto 区(模板,不要重写)**:不变的控制面。node 的 Auto 区包括 `## Node Goal`、`## Entry Check`、`## Skill Implementation`、`## Required Skill Calls`、`## Output Schemas`、`## Evidence Record`、`## Guardrails`、`## Exit Check`、`## Recovery`。entry 的 Auto 区包括 `## Workflow Nodes`、`## Skill Bindings`、`## Guardrails And Evidence`、`## Runtime And Recovery`。 +- **Authored 区(你写)**:Auto 区无法得知的领域决策。这是让 Skill "好用"而非"只是能跑"的关键。 + +## 样例:`substance` 节点的 Guidance 区(workflow-kernel) + +下面是 research-writer workflow 中"Research"生产者节点的 `## Guidance` 正文。注意它是**决策内容**:前提、有序的领域步骤、超出机械 Exit Check 的完成判断、Red flags。它按名引用绑定的 Skill,但不复制其正文。 + +```markdown +## Prerequisites + +- entry 的 Decision Core 已确认研究主题与范围。 +- `research-skill` 在项目 Skill 池中可解析;若缺失,先停下询问用户,不要临时替代。 + +## Steps + +1. 加载 `research-skill` 并按其发现方法处理已确认主题。当项目 Skill 定义了特定来源顺序时,不要用通用网络搜索替代。 +2. 按优先级收集来源;为每个来源记录来源、日期和你要复用的论点。不通过项目可信度门槛的来源应直接剔除,而不是标注为"偏弱"。 +3. 把发现提炼到 `notes/*.md` 笔记文件——每条独立论点一份,含逐字引用与来源指针。综合写在 writer 节点,不在这里。 +4. 记录 `research.notes.v1` 的 `summary` evidence:一段提炼 + 产出的笔记数量。 + +## Completion reasoning + +本节点完成当且仅当两条同时成立:(a) 已记录 `summary` evidence;(b) 至少一个 artifact 匹配 `notes/*.md`。不要仅仅因为步骤清单走完就退出——如果相对主题范围笔记仍偏稀疏,应继续研究而非宣布完成。Exit Check 脚本会机械地强制 artifact + evidence 要求;你的职责是判断研究是否真正充分。 + +## Red flags + +- 记录了 `summary` 却没有产出任何 `notes/*.md` 就退出(guardrail 会阻塞——不要试图绕过)。 +- 把来源原文复制进笔记却不加引用标记或来源指针。 +- 某来源仍"待核实"就推进到 Write 节点——核实属于本节点。 +- 对需要多视角的主题,仅凭单一来源就认为充分。 +``` + +用 `###` 子标题,使其嵌套在 `## Guidance` 之下。上述四段(Prerequisites / Steps / Completion reasoning / Red flags)是 `substance` 节点的预期形态。 + +## 样例:entry Decision Core(workflow-entry) + +下面是 entry SKILL.md 的 `## Decision Core` 正文。entry 是**每次调用最先读取的文件**——一个薄 Decision Core("跑 next,照做")会让整个 Skill 感觉机械。一个富 Decision Core 是 comet 级 Skill "好用"的核心。 + +Decision Core 应建模 Auto 区不处理的三件事:(1) **语义化当前节点检测**(如何判断用户在哪个 Node,而非只看脚本输出),(2) **resume 与 drift 规则**(上下文恢复或状态与文件冲突时怎么办),(3) **决策点与 Red flags**(何时暂停等用户,以及什么是假进展)。 + +```markdown +### 自动节点检测 + +**Step 0:确定当前节点与意图** + +1. 检查 workflow protocol 的有序 Node 列表。第一个未完成(无 Exit evidence 记录)的 Node 是候选当前节点。 +2. 若用户描述的工作明显属于更后面的 Node(如"验证结果"但研究尚未完成),暂停并说明:前序 Node 必须先完成。不要跳过。 +3. 若用户描述的工作属于已标记完成的更早 Node,视为纠正——重置该 Node 的完成状态并重新进入。 + +**Step 1:读取 workflow 状态** + +运行 `node "$WORKFLOW_STATE" status` 确认检测到的节点。若脚本的 `NEXT:` 输出与文件证据冲突(如脚本说 DONE 但无 artifact),以文件为准,先纠正状态再继续。 + +**Resume 规则**: +- 每次上下文恢复,重新执行 Step 0 和 Step 1。不要信任对话历史做节点检测。 +- 若状态显示某 Node 已完成但预期 artifact 缺失,视为未完成并重新进入。 +- 若用户在某个 Node 中途恢复但话题变了,确认是继续当前 Node 还是开始新的。 + +### 决策分类与决策点 + +先分类再行动:有两个或以上会改变范围、行为、风险接受或不可逆结果的合法选项才是用户决策;唯一安全下一步直接执行;缺少依赖、状态损坏或无法继续的 guard 失败是停止条件;`NEXT: manual` 只是交还控制权。只有第一类必须暂停。 + +| 情况 | 处理 | +|------|------| +| 首次调用且主题/范围明确 | 自动初始化状态并进入第一个 Node;不得为了确认已知信息而停顿 | +| 主题、范围或目标 Node 存在两个以上互斥且合法的解释 | 合并为一个问题,让用户选择;不要猜测 | +| Node 需要用户确认输出才能推进 | 记录 evidence 后停下;等待明确确认 | +| WARNING/偏差接受或不可逆发布存在真实取舍 | 只展示当前可执行选项,并记录用户选择 | + +Node guard 失败时先自动读取证据并执行唯一安全修复;若缺少依赖或状态损坏导致无法继续,报告停止条件和恢复要求。只有恢复方式存在多个会改变范围或风险的合法选项时,才升级为上表中的用户决策。 + +### Red Flags + +| Agent 想法 | 实际风险 | +|-----------|---------| +| "首次调用都应该再确认一次" | 清晰输入不需要重复批准;只有互斥解释仍会改变范围时才询问。 | +| "脚本返回 NEXT: auto,应该立即加载下一个 Skill" | `NEXT: auto` 表示 Node 完成,不是跳过确认。检查下一个 Node 是否有决策点。 | +| "guard 失败,所以让用户决定怎么办" | 先自动诊断并执行唯一安全修复;无合法动作时报告停止条件,不要发明选项。 | +| "看起来和上次一样的话题,从上次断点继续" | 始终重新读取状态。对话记忆在上下文压缩后不可靠。 | +| "Exit Check 通过了,所以工作够好了" | Exit Check 是机械的。你的职责是判断检查之外的质量——稀疏笔记、浅层分析、缺失视角,脚本抓不到。 | +``` + +此样例以精简形式建模了 comet 的 Decision Core:语义检测(Step 0 读 Node 顺序,非只看脚本输出)、状态忠实(文件优先于过期状态)、resume 规则(每次恢复重新检测)、阻塞决策点(显式表格)、Red flags("想法 → 风险"模式,抓 agent 自欺)。 + +## substance 与 delegates + +- **substance** 节点(workflow-kernel):上面的样例就是标尺。必须有富 Guidance;缺失时该节点渲染为 `AUTHORING PENDING`,Bundle 不得 ready。 +- **delegates** 节点(comet-five-phase-overlay,委托给已安装的富 Skill):富执行内容由被委托 Skill 承载,**不要复制**。但如果该节点声明了 **Required Skill Calls**(如 execute 节点要求 `elementui`),请写一段聚焦的整合说明——在被委托流程的什么时机必须加载该 Skill、它补充什么 evidence——而不是泛泛一句"加载 X"。例如要求 `elementui` 的 delegates execute 节点: + +```markdown +本节点用 `comet-build` 执行。在其流程之外,每当改动涉及组件库时加载 `elementui`,并在记录 `required-skill:execute.elementui` 检查前确认改动使用了项目认可的组件。不要重复实现 `comet-build` 已做的事。 +``` + +## 反模式(不要写) + +- 一行式 Guidance,如"运行本节点并记录 evidence。"——Auto 区已隐含此意,毫无增量。 +- 整段复制被绑定 Skill 的正文。 +- 重述路由表或 Output Schema 列表(Auto 区已有)。 +- substance 节点没有 `### Red flags`——Red flags 是大部分真实价值所在。 diff --git a/.pi/skills/comet-any/reference/authoring-subagents.md b/.pi/skills/comet-any/reference/authoring-subagents.md new file mode 100644 index 0000000..842ac01 --- /dev/null +++ b/.pi/skills/comet-any/reference/authoring-subagents.md @@ -0,0 +1,107 @@ +# Authoring Subagents 总览 + +## 核心原则 + +`/comet-any` 的创作成果应由平台原生 subagent 分工产出,再交给主会话组装和后端 CLI 记录状态。 +Claude Code、Codex、Gemini、Copilot 等平台的 subagent 调用方式不同,本参考只定义职责、输入和输出; +具体派发方式使用当前平台提供的原生 subagent 机制。 + +`reference/subagents/*.md` 是跨平台 lane brief;Claude Code custom agent 必须单独生成到平台 agent 资源,并带 `name`、`description`、`tools`、`model` frontmatter。不要把这些 portable lane brief 直接当作 platform-native custom agent 安装。 + +先读取本总览,再只把对应角色 brief 交给对应 subagent。不要把六个角色 brief 合并成一个大 prompt; +主会话负责保留全局上下文、汇总成果、调用 `comet bundle` 和 `comet publish`,subagent 只产出可审查草稿。 + +平台支持 subagent 时必须调度。没有平台 subagent 能力时,主会话可以使用同一份 brief 以内联方式完成, +但必须在用户可读摘要和 `reference/authoring-lanes.json` 中标记为 fallback。 + +所有 subagent 只返回 Markdown 成果和结构化审查结论,不得直接写入 Bundle state,不得执行候选 Skill 的脚本, +不得运行发布、安装或分发命令。CLI 状态仍由主会话维护。 + +## 角色 brief + +在用户确认 Skill Creator 方案后、运行 `comet creator generate` 或生成源码前,主会话按以下顺序读取并派发: + +1. 脚本作者 subagent:`comet-any/reference/subagents/script-author.md` +2. reference 作者 subagent:`comet-any/reference/subagents/reference-author.md` +3. workflow entry 作者 subagent:`comet-any/reference/subagents/workflow-entry-author.md` +4. Skill 核心作者 subagent:`comet-any/reference/subagents/skill-core-author.md` +5. 停顿点作者 subagent:`comet-any/reference/subagents/pause-points-author.md` +6. Skill 审查 subagent:`comet-any/reference/subagents/skill-reviewer.md` + +角色文件在本 Skill 内的相对路径是: + +- `reference/subagents/script-author.md` +- `reference/subagents/reference-author.md` +- `reference/subagents/workflow-entry-author.md` +- `reference/subagents/skill-core-author.md` +- `reference/subagents/pause-points-author.md` +- `reference/subagents/skill-reviewer.md` + +这些 subagent 的成果先落为可审查草稿,再进入 `reference/authoring-lanes.json`、`reference/skill-review.md` +和最终 Bundle draft。若任一 subagent 报告 blocking finding,必须停在草稿修复,不得继续 ready。 + +## 按 DAG 派发 + +上面的角色 brief 顺序只是创作 DAG 的线性展开,并非要求严格串行。权威 DAG 在 `reference/authoring-protocol.json` 与 `comet creator authoring-plan --depth quick|full --json`: + +- **wave1**(`script`、`reference`、`pause-points`):彼此无依赖。在支持子代理的平台上并发派发这三个。每个只拿到自己的角色 brief、通用输入和 protocol/resolved-skills 路径(文件交接,不共享历史)。 +- **wave2**(`workflow-entry`、`skill-core`):依赖 script 契约(`NEXT:`/`SKILL:` 输出)。仅在 script lane DONE 后开始。两者之间可并发。 +- **barrier**(`skill-review`):唯一同步点。仅在 wave1 与 wave2 全部 DONE 后运行;审查者必须读取所有 artifact 与 claim。 + +无论平台如何: + +- 编排遵循 DAG 依赖;只有 barrier 真正等待此前所有 lane。 +- 不支持子代理的平台,主会话按依赖顺序内联执行同样的 lane——语义完全一致,仅延迟不同。在 `reference/authoring-lanes.json` 中按 lane 记录 `dispatchMode: "subagent"` 或 `"inline"`。 +- Claude Code 可把某个 wave 的扇出委托给其 `Workflow` 工具作为可选加速器;这是实现选择,不是契约的一部分。契约是 protocol + schemas + DAG,任何平台都能解释。 +- 每个 lane 产出在下一个依赖 wave 开始前,必须经 `comet creator authoring-record --lane --file --json` 校验并记录。 + +## 通用输入 + +每个 subagent 都必须拿到同一组上下文: + +- 用户确认的目标、起点和语言。 +- `plan.json` 中的 `goal`、`workflow.kind`、`workflow.nodes`、`engineMode`、`runnerMode`,以及规范化后的 `workflow-protocol.json`。 +- `reference/resolved-skills.json` 或等价的真实 Skill 来源摘要。 +- `reference/workflow-protocol.json` 或即将写入该文件的 workflow protocol。 +- `/comet-classic` 定制场景下的受保护边界:`open / design / build / verify / archive`、`.comet.yaml`、decision point、verify-result-transition、archive-delta-sync。 +- 项目级偏好、缺失/歧义候选处理结果、偏离原因、scripts/hooks 可执行披露。 + +## 输出格式 + +每个 subagent 返回: + +```json +{ + "lane": "", + "artifacts": [ + { + "path": "reference/example.md", + "kind": "reference", + "content": "..." + } + ], + "claims": [ + { + "id": "reference:example", + "kind": "reference", + "paths": ["reference/example.md"], + "summary": "说明该成果保证了什么" + } + ], + "findings": [] +} +``` + +`claims` 是审查依据,不是装饰字段。缺少关键 claim 时,Skill 审查 subagent 必须阻塞。 + +## 派发注意事项 + +- 每次派发必须创建新的 subagent,不得继承主会话历史。主会话只提供该角色需要的 brief、输入路径和必要背景。 +- 必须显式指定 model;平台不支持 model 选择时,在 `reference/authoring-lanes.json` 记录为 platform-default。 +- 使用文件交接:主会话提供路径,不粘贴大段全文。通用输入、来源 Skill 摘要、草稿 artifact 和报告都应以路径交接。 +- 每个 subagent 只接收自己的角色 brief、通用输入和必要 artifact,不接收其他角色的完整 brief。 +- Skill 审查 subagent 必须在其他五个作者角色产出后运行,并读取所有 artifacts 与 claims。 +- 主会话可以要求某个角色返工,但返工结果仍必须回到 `reference/authoring-lanes.json` 和 `reference/skill-review.md`。 +- subagent 不能调用 `comet bundle`、`comet publish`、`comet skill`,也不能执行候选 Skill 的脚本。 +- 如果状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,主会话必须补上下文、拆小任务、换更强模型或询问用户;不得继续组装。 +- 没有平台 subagent 能力时,内联 fallback 必须保留相同的 lane、claim 和 finding 结构。 diff --git a/.pi/skills/comet-any/reference/bundle-authoring.md b/.pi/skills/comet-any/reference/bundle-authoring.md new file mode 100644 index 0000000..c327f1e --- /dev/null +++ b/.pi/skills/comet-any/reference/bundle-authoring.md @@ -0,0 +1,166 @@ +# Bundle Authoring 参考 + +`comet creator` 是 `/comet-any` 的普通 CLI 表面,背后复用 `comet bundle` 的确定性后端。普通用户不需要记忆 Bundle 子命令;Skill Creator 负责把用户确认的 Workflow Contract 写成 plan,再调用 CLI 维护状态。 + +## Workflow Contract 输入 + +新的 `plan.json` 使用 `workflow` 作为主输入: + +```json +{ + "goal": "基于 /comet-classic 的五阶段定制,要求组件库和白盒审查。", + "skillCreatorIntent": "customize-comet", + "workflow": { + "kind": "comet-five-phase-overlay", + "name": "team-comet", + "goal": "要求组件库和白盒审查。", + "nodes": { + "execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "reason": "Use project component library during direct implementation." + } + ] + }, + "subagent-execute": { + "requiredSkillCalls": [ + { + "skill": "elementui", + "scope": "handoff" + } + ] + }, + "review": { + "requiredSkillCalls": [ + { + "skill": "whitebox-code-standard", + "scope": "review" + } + ] + } + } + }, + "engineMode": "deterministic", + "runnerMode": "standalone" +} +``` + +字段约定: + +- `workflow.kind`: `comet-five-phase-overlay` 或 `workflow-kernel`。 +- `workflow.nodes`: 按 Node id 覆盖或增强 Workflow Node。 +- `implementation`: 替换 Node implementation。只能用于允许 override 的 Node。 +- `requiredSkillCalls`: Required Skill Call,要求 Node 内必须调用 Skill。 +- `augmentations`: 增强 Node,不替换主 implementation。每个新增 binding 或 schema 都必须声明 enforcement:`guarded`、`handoff-guarded`、`evidence-only` 或 `advisory`。 +- `satisfies`: producer override 满足的 Output Schema id。 +- `outputSchemas`: 高级 `workflow-kernel` 可以声明自定义 Output Schema。Output Schema 必须挂到具体 Workflow Node 才算生效;只定义在 `workflow.outputSchemas` 里不会触发 guard、eval 或 readiness。 +- `customNodes[].responsibility`: 自定义 Node 必须声明它在 Agent workflow 中承担的职责。 +- `customNodes[].requiredSkillCalls`: 自定义 Node 可直接声明必须调用的 Skill,生成脚本会按 evidence 阻断。 + +## 内置 Comet Nodes + +`comet-five-phase-overlay` 内置这些 Workflow Node: + +- `comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`;没有 active change 或多个 active changes 时必须阻塞并请用户选择。 +- 不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态;overlay 脚本只能读取并推进 `.comet.yaml`,其他文件只能作为 draft、eval 或 publish evidence。 +- `open`: control,保留 Comet intake 和 `.comet.yaml` 初始化。 +- `design`: producer,可 override,但必须满足 `comet.design.v1`。 +- `plan`: producer,可 override,但必须满足 `comet.plan.v1`。 +- `execute`: control,可 require / augment,例如要求 `elementui`。 +- `subagent-execute`: Handoff,可要求子代理使用 `elementui` 并回传 evidence。 +- `review`: Guardrail,可要求 `whitebox-code-standard`。 +- `verify`: control,保留验证和分支收尾。 +- `archive`: control,保留 OpenSpec archive 和 delta sync。 + +自定义 `workflow-kernel` Node 示例: + +```json +{ + "id": "delegate-notes", + "label": "Delegate Notes", + "kind": "handoff", + "responsibility": "Delegate release note drafting and require returned evidence.", + "implementation": { + "skill": "handoff-coordinator", + "scope": "handoff" + }, + "requiredSkillCalls": [ + { + "skill": "release-notes", + "scope": "handoff" + } + ], + "operations": ["require", "augment"], + "outputSchemas": ["release.notes.v1"], + "guardrails": [ + { + "id": "handoff-returned", + "label": "Handoff returned evidence", + "validation": "semantic" + } + ] +} +``` + +## 生成物 + +Factory 必须生成: + +- entry `SKILL.md` +- 每个 Workflow Node 对应的 internal Skill +- `reference/workflow-protocol.json` +- `reference/resolved-skills.json` +- `reference/decision-points.md` +- `reference/recovery.md` +- `reference/authoring-lanes.json` +- `reference/skill-review.md` +- `scripts/workflow-state.mjs` +- `scripts/workflow-guard.mjs` +- `scripts/workflow-handoff.mjs` +- `scripts/comet-plan.mjs` +- `scripts/comet-check.mjs` +- `scripts/comet-hook-guard.mjs` +- `comet/eval.yaml` + +三个 `workflow-*.mjs` 按 workflow 契约创作;三个 `comet-*.mjs` 由 factory 确定性生成(plan 别名、必需文件检查、hook guard),同样读取 `workflow-protocol.json`。 + +`workflow-protocol.json` 是 runtime、eval、review、publish readiness 的共同事实源。 + +## CLI 生命周期 + +常用 Creator 命令: + +```bash +comet creator guide --project . --json +comet creator candidates --json +comet creator propose --file --json +comet creator init --file --confirmed-proposal --json +comet creator resolve --candidate --source --json +comet creator generate --json +comet bundle compile --platform --json +comet eval /comet/eval.yaml --quick --html +comet eval /comet/eval.yaml --full --html +comet publish review --platform --json +comet publish approve --reviewer --json +comet publish run --platform --json +comet publish distribute --platform --scope project --preview --json +``` + +`` 是 compile 后生成 Skill 的目录。eval 结果必须登记为当前 draft hash 的 eval evidence;readiness 只读取该 hash 对应的证据,旧 hash 证据只能保留作审计,不能推进 ready。 + +## Readiness + +readiness 必须检查: + +- resolved Skills 无 missing / ambiguous。 +- proposal 已确认。 +- `workflow-protocol.json` 可加载。 +- control Node 没有普通 override。 +- producer override 满足 Output Schema。 +- 每个新增 binding 或 schema 都有 `guarded`、`handoff-guarded`、`evidence-only` 或 `advisory` enforcement。 +- 当前 draft hash 的 eval evidence 已存在且未过期。 +- 人工 review 已批准当前 hash。 +- capability gap 和 executable disclosure 已展示。 + +非 JSON 输出也必须向用户展示 `Readiness:`、`Blockers:`、`Warnings:`、`Evidence:`。 diff --git a/.pi/skills/comet-any/reference/eval-provider.md b/.pi/skills/comet-any/reference/eval-provider.md new file mode 100644 index 0000000..c954f05 --- /dev/null +++ b/.pi/skills/comet-any/reference/eval-provider.md @@ -0,0 +1,66 @@ +# Eval Evidence 参考 + +## Eval 选择 + +在任何验证动作前,必须展示: + +- `quick` 的预计运行次数、覆盖组件和 token 消耗。 +- `full` 的预计运行次数、覆盖组件和 token 消耗。 +- `skip / quick / full eval` 三个选择。 + +skip、eval 失败或证据 hash 过期时不得进入 ready;也不得 publish 或 distribute。 + +普通用户的验证路径保持单一:日常评估统一走 `comet eval`。`/comet-any` 可以在内部登记 eval evidence,但不要把内部登记步骤包装成面向普通用户的替代命令。对普通用户,解释时优先使用“验证”,不要把 `Publish readiness:` 当成第一层概念。 + +## 结果记录 + +eval evidence 必须输出结构化 JSON,至少包含: + +- 当前 draft hash。 +- 覆盖的 entry Skill 和 internal Node Skill。 +- `workflow-protocol.json` hash。 +- quick 或 full 的选择、token 消耗和结果摘要。 +- Bundle 编译、安全检查和 capability evidence。 + +只有当前 draft hash 的 eval evidence 可以推进状态。旧 hash 证据可以保留在磁盘用于审计,但不能让当前 draft 进入 ready。 + +## 人工评审 + +eval 通过后仍必须人工批准。评审摘要至少包含: + +先运行 `comet publish review --platform --json`,再基于其输出展示: + +- Bundle 名称、版本、hash。 +- 多个 entry 与 internal Skill 列表。 +- `planHash`、`preferenceHash` 与 `reference/resolved-skills.json` 真实 Skill 证据,包括项目级偏好模式、required Skill、`sourceSummaries` 与“整理后的工作方式”摘要。 +- 推荐调用顺序与 `preferenceIndex`。 +- 偏离偏好顺序的项和原因。 +- `.comet/skill-preferences.yaml` 是否在 Factory 初始化后发生漂移;`advisory` 模式给出 warning,`strict` 模式阻塞。 +- 稳定组合 Skill Bundle 的 required capability set(必需能力集合)`skills/scripts/rules/hooks/references` 是否声明完整,且 `scripts/rules/hooks` 是否继续作为 required control plane。 +- 是否生成 `comet/skill.yaml`、`comet/guardrails.yaml`、`comet/checks.yaml` 与 `comet/eval.yaml`。 +- `hooks/*.yaml` 是否仅被当作 portable hook descriptor,等待 `comet publish distribute` 编译到目标平台。 +- 能力缺口和可执行披露。 +- eval 选择、token 消耗和结果摘要。 +- `Validate this Skill` 与下一步提示,让用户知道 readiness 为什么可发布或被阻塞。 + +readiness blockers 会阻止 publish。只要存在当前 draft hash 的 eval evidence 缺失、人工 approval 缺失、required capability gap 或 executable disclosure 未确认,就必须停在评审阶段,不能继续发布。 + +只有用户显式批准后,才能运行 `comet publish approve` 并发布。 + +## 分发预览 + +执行真实分发前,必须先跑 preview: + +```bash +comet publish distribute --platform --scope project --preview --json +``` + +preview 是强制检查,不是可选附加项。它应向用户展示: + +- `Install preview` +- planned files +- unsupported capability +- executable disclosures +- `No files were written` + +只有当用户确认 preview 结果后,才可以移除 `--preview` 执行真实分发。 diff --git a/.pi/skills/comet-any/reference/subagents/pause-points-author.md b/.pi/skills/comet-any/reference/subagents/pause-points-author.md new file mode 100644 index 0000000..f4fb306 --- /dev/null +++ b/.pi/skills/comet-any/reference/subagents/pause-points-author.md @@ -0,0 +1,81 @@ +# 停顿点作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +设计用户在 Skill 中真正必须选择的位置,以及跨设备断点恢复方式。必须先区分四类情况:用户决策、自动处理、停止条件和手动衔接。只有存在两个或以上会改变范围、行为、风险接受或不可逆结果的合法选项时,才创建用户停顿点;单一安全动作直接执行,缺少依赖或状态损坏只报告停止条件,手动衔接只交还控制权。真正的用户决策不能被默认推荐、历史偏好或自动推进绕过。 + +必须覆盖: + +- `reference/decision-points.md` +- `reference/recovery.md` + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- Skill Creator 方案确认页中的 `confirm-generate`、`revise-proposal`、`cancel`:这是会改变生成结果的用户决策。 +- eval 工作量选择 `skip / quick / full eval` 和安装前人工批准:只有确实存在多个合法选项时才是用户决策。 +- 当前 draft hash 的 eval evidence 缺失或过期、unresolved、ambiguous、capability gap、executable disclosure:逐项判断是可自动修复、无路可走的停止条件,还是确有多个恢复选项的用户决策;不得统一写成停顿点。 +- runner 恢复状态和跨设备恢复入口:复用仍有效的持久化选择,不得在恢复时重复询问。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、 +workflow protocol 和已有草稿。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "设计 的用户停顿点和恢复" +model: <必须显式指定 model> +prompt: + 你是停顿点作者 subagent。 + 先读取本 brief、通用输入路径、workflow protocol 路径、Skill 草稿路径和报告文件路径。 + 开始前先把候选节点分类为用户决策、自动处理、停止条件或手动衔接。如果分类所需事实缺失,先返回 NEEDS_CONTEXT。 + 不要猜测缺失选择,也不要把自动修复、guard 失败、能力缺口、单一合法动作或手动衔接伪装成用户停顿点。 + 只产出 decision-points 和 recovery 草稿,不写 Bundle state,不执行候选脚本。 + 把完整停顿点草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回草稿时先列分类表,再说明真正的用户停顿点: + +- 每个候选节点属于用户决策、自动处理、停止条件还是手动衔接,以及判定依据。 +- 每个停顿点的触发条件。 +- 用户可选择项。 +- 每个选择会进入哪个阶段。 +- 停顿点证据写入哪里。 +- 自动处理如何直接推进;停止条件如何报告恢复条件而不发明选项。 +- 恢复时如何显示当前阶段、阻塞原因、建议下一步和真实可选项,并复用已持久化选择。 + +停顿点必须适配当前 workflow protocol,不得只列 Comet 原始停顿点。 + +## 自检 + +返回前逐项检查: + +- 每个用户停顿点都有触发条件、选项、下一阶段和证据位置。 +- 每个停顿点至少有两个当前可执行的合法选项;相邻且可同时回答的选择已合并,单一合法值不会制造停顿。 +- guard 失败、确定性重试、状态对账、能力缺口和 `NEXT: manual` 已按实际情况归入自动处理、停止条件或手动衔接,而不是默认询问用户。 +- 默认推荐、历史偏好和自动推进都不能绕过必须停顿点。 +- 恢复摘要能显示当前阶段、阻塞原因、建议下一步和真实可选项,且不会重复询问仍有效的选择。 +- 跨设备恢复不依赖当前会话记忆。 +- 停顿点适配当前组合 Skill,不只是照列 Comet 原始停顿点。 + +## 必须返回的 claim + +- `pause:decision-points` +- `pause:recovery` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、未解决疑虑和恢复风险。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.pi/skills/comet-any/reference/subagents/reference-author.md b/.pi/skills/comet-any/reference/subagents/reference-author.md new file mode 100644 index 0000000..434b986 --- /dev/null +++ b/.pi/skills/comet-any/reference/subagents/reference-author.md @@ -0,0 +1,81 @@ +# reference 作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写和整理候选 Skill 的 reference 层,让审计、恢复、评审和后续维护有真实依据。 +reference 层承载证据,不把内部审计内容塞进用户可见 `SKILL.md`。 + +必须覆盖: + +- `reference/workflow-protocol.json` +- `reference/resolved-skills.json` +- `reference/composition-report.md` +- `reference/authoring-lanes.json` + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- resolved Skill 的名称、来源、description、hash、reference、rules、scripts、hooks。 +- `sourceSummaries`,必须来自真实 `SKILL.md` 正文和直接 reference。 +- 项目级偏好、缺失/歧义候选处理、偏离原因和可执行披露。 +- `/comet-classic` 定制时保留的五阶段语义。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要读取主会话历史,也不要把来源 Skill 原文整段搬进 +reference。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "整理 的 reference 证据" +model: <必须显式指定 model> +prompt: + 你是 reference 作者 subagent。 + 先读取本 brief、通用输入路径、resolved skills 路径、workflow protocol 路径和报告文件路径。 + 开始前先提出问题:如果来源证据、hash、偏好决策或可执行披露不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失来源。 + 只产出 reference 草稿和 claims,不写 Bundle state,不执行候选脚本。 + 把完整 reference 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回 reference 草稿,说明: + +- workflow protocol 中每个阶段的目标、守卫、下一阶段条件和恢复点。 +- resolved Skill 证据如何支持组合流程。 +- 组合与用户偏好哪里一致、哪里偏离、为什么偏离。 +- 哪些内容属于用户可见流程,哪些内容只保留在 reference 审计层。 + +不要把 reference 写成原 Skill 的复制粘贴;必须提炼为组合 Skill 可用的证据和协议。 + +## 自检 + +返回前逐项检查: + +- 每个 source summary 都能追溯到真实 `SKILL.md` 或直接 reference。 +- workflow protocol 能解释阶段目标、守卫、自动推进和恢复。 +- 偏好、缺失、歧义、偏离和可执行披露都有记录。 +- 用户可见流程和内部审计内容边界清楚。 +- 没有把原 Skill 全文复制进 reference。 + +## 必须返回的 claim + +- `reference:workflow-protocol` +- `reference:resolved-skills` +- `reference:composition-report` +- `reference:authoring-lanes` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、证据缺口和疑虑。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么来源、 +尝试过什么、需要主会话如何处理。 diff --git a/.pi/skills/comet-any/reference/subagents/script-author.md b/.pi/skills/comet-any/reference/subagents/script-author.md new file mode 100644 index 0000000..2fbe26a --- /dev/null +++ b/.pi/skills/comet-any/reference/subagents/script-author.md @@ -0,0 +1,84 @@ +# 脚本作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +设计候选 Skill 的脚本契约,而不是复制 Comet Classic 的脚本。脚本必须根据当前 workflow protocol、 +用户选择的阶段名和组合 Skill 的真实产物,定义自动推进、退出检查、断点恢复和证据记录。 + +必须覆盖: + +- `scripts/workflow-state.mjs` +- `scripts/workflow-guard.mjs` +- `scripts/workflow-handoff.mjs` + +factory 还会从同一份 `workflow-protocol.json` 确定性生成 `scripts/comet-plan.mjs`、`scripts/comet-check.mjs`、`scripts/comet-hook-guard.mjs`。不要重复设计这三个脚本;你的契约只针对 skill-core 作者与 entry 作者引用的 `workflow-*.mjs`。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- `reference/workflow-protocol.json` +- `plan.json` 的 `workflow.kind`、`workflow.nodes`、`engineMode`、`runnerMode`,以及规范化后的 `workflow-protocol.json` +- `reference/resolved-skills.json` +- `/comet-classic` 定制时的 `.comet.yaml` 受保护语义;`comet-five-phase-overlay` 的主状态只来自 `openspec/changes//.comet.yaml`,不得创建 `.comet/runs//state.json` 作为 Comet overlay 主状态 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要读取主会话历史,也不要要求用户重新解释已经写入 +artifact 的内容。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的脚本契约" +model: <必须显式指定 model> +prompt: + 你是脚本作者 subagent。 + 先读取本 brief、通用输入路径、workflow protocol 路径、resolved skills 路径和报告文件路径。 + 开始前先提出问题:如果脚本边界、Node 完成条件、状态写入或恢复语义不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失协议。 + 不要调用 comet bundle、comet publish、comet skill,也不要执行候选 Skill 的脚本。 + 把完整脚本契约写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回脚本契约草稿,说明每个脚本: + +- 读取哪些状态。 +- 写入哪些状态和 evidence。 +- 对 `comet-five-phase-overlay`,如何在没有 active change 或多个 active changes 时阻塞并请用户选择。 +- 如何判断当前阶段目标是否完成。 +- 如何输出 `NEXT:`、`SKILL:` 和阻塞原因。 +- 如何在阶段未完成时继续停留,而不是强制退出。 +- 如何支持跨设备断点恢复。 + +退出检查必须来自当前 workflow protocol 和组合 Skill 的目标,不得照搬 Comet Classic 脚本。 + +## 自检 + +返回前逐项检查: + +- 每个脚本都有输入、输出、状态读写、evidence 写入和失败行为。 +- Node 完成条件来自 workflow protocol,而不是来自固定阶段名。 +- `NEXT:` 和 `SKILL:` 的输出条件可被 Skill 核心作者直接引用。 +- 跨设备恢复不依赖当前会话记忆。 +- 没有执行候选脚本,也没有写入 Bundle state。 + +## 必须返回的 claim + +- `script:workflow-state` +- `script:workflow-guard` +- `script:workflow-handoff` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、测试/校验说明和疑虑。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.pi/skills/comet-any/reference/subagents/skill-core-author.md b/.pi/skills/comet-any/reference/subagents/skill-core-author.md new file mode 100644 index 0000000..94cd1b3 --- /dev/null +++ b/.pi/skills/comet-any/reference/subagents/skill-core-author.md @@ -0,0 +1,129 @@ +# Skill 核心作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写 internal Node Skill 的用户可见核心内容。entry Skill 由 workflow entry 作者单独负责。目标是产出 Comet-like 的多 Node workflow, +不得把来源 Skill 简单串联,也不复制粘贴原 Skill 全文。 + +必须覆盖: + +- 每个 internal Node Skill +- `comet/skill.yaml` 中的阶段调用语义 +- 每个 `node-skill:` claim + +## 创作区(你写什么) + +生成器把每个 Node SKILL.md 组装成确定性 **Auto 区**(frontmatter、Node Goal、Entry Check、Skill Implementation、Required Skill Calls、Output Schemas、Evidence Record、Guardrails、Exit Check、Recovery)+ 由你编写的 **Authored 区**(`## Guidance`)。**你只写 Guidance 正文,不写整个文件**。主会话通过 `comet creator authoring-record --lane skill-core --file ` 记录你的产出;artifact `..//SKILL.md` 的 `content` 即 Guidance 正文。 + +质量标尺:真实的 Comet 阶段 skill(如 `comet-build/SKILL.md`);完整 substance 节点 Guidance 范例见 `reference/authored-zone-example.md`。写决策内容,不写套话。在 Guidance 内用 `###` 子标题(嵌套在 `## Guidance` 之下): + +- `### Prerequisites` — 本 Node 开始前必须成立的前提。 +- `### Steps` — 有序、领域相关的步骤;按名引用绑定的 Skill 并说明何时调用(不要复制它的正文)。 +- `### Completion reasoning` — 本 Node 真正完成的判定(超出机械的 Exit Check),以及其中的判断取舍。 +- `### Red flags` — 看似进展、实则不然的失败模式。 + +下面是"Research" substance 节点的具体摘录,展示预期深度。注意它是**决策内容**(何时停止、什么算充分、什么该拒绝),不是重述路由表或 output schema。 + +```markdown +### Prerequisites + +- entry 的 Decision Core 已确认研究主题与范围。 +- `research-skill` 在项目 Skill 池中可解析;若缺失,先停下询问用户,不要临时替代。 + +### Steps + +1. 加载 `research-skill` 并按其发现方法处理已确认主题。当项目 Skill 定义了特定来源顺序时,不要用通用网络搜索替代。 +2. 按优先级收集来源;为每个来源记录来源、日期和你要复用的论点。不通过项目可信度门槛的来源应直接剔除,而不是标注为"偏弱"。 +3. 把发现提炼到 `notes/*.md` 笔记文件——每条独立论点一份,含逐字引用与来源指针。综合写在 writer 节点,不在这里。 +4. 记录 `research.notes.v1` 的 `summary` evidence:一段提炼 + 产出的笔记数量。 + +### Completion reasoning + +本节点完成当且仅当两条同时成立:(a) 已记录 `summary` evidence;(b) 至少一个 artifact 匹配 `notes/*.md`。不要仅仅因为步骤清单走完就退出——如果相对主题范围笔记仍偏稀疏,应继续研究而非宣布完成。Exit Check 脚本会机械地强制 artifact + evidence 要求;你的职责是判断研究是否真正充分。 + +### Red flags + +- 记录了 `summary` 却没有产出任何 `notes/*.md` 就退出(guardrail 会阻塞——不要试图绕过)。 +- 把来源原文复制进笔记却不加引用标记或来源指针。 +- 某来源仍"待核实"就推进到 Write 节点——核实属于本节点。 +- 对需要多视角的主题,仅凭单一来源就认为充分。 +``` + +节点模式(来自 protocol): + +- **substance** 节点(workflow-kernel 默认):必须有富 Guidance。缺失时该节点渲染为 `AUTHORING PENDING`,Bundle 不得 ready。 +- **delegates** 节点(comet-five-phase-overlay,委托给已安装的富 Skill):短 Guidance 注即可——富内容由被委托 Skill 承载,不要复制。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- 用户确认的阶段名和可输入名字项。 +- `reference/workflow-protocol.json` 的阶段目标、`requiredSkillCalls` 与自动推进条件。 +- `reference/resolved-skills.json` 的真实 Skill 摘要。 +- 脚本作者返回的 `NEXT:`、`SKILL:`、guard 和 recovery 契约。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、 +脚本契约和 reference 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的 Skill 核心内容" +model: <必须显式指定 model> +prompt: + 你是 Skill 核心作者 subagent。 + 先读取本 brief、通用输入路径、脚本契约路径、reference 证据路径和报告文件路径。 + 开始前先提出问题:如果阶段名、必须调用的 Skill、自动推进或用户停顿点不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失流程。 + 只写 internal Node Skill 草稿,不写 entry Skill,不写 Bundle state,不执行候选脚本。 + 把完整 Skill 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +返回 internal Node Skill 草稿,必须体现: + +- internal Node Skill 负责单 Node 目标、必须调用的 Skill、Node 完成证据和脚本守卫。 +- 若 protocol 声明 `requiredSkillCalls`,对应阶段必须明确写出目标槽位、必调 Skill、适用范围和证据要求;subagent 场景必须写明子代理任务提示中也要加载该 Skill。 +- 阶段未达成目标时继续工作,不因为流程清单走完就退出。 +- 自动推进必须来自脚本输出的 `NEXT:` 和 `SKILL:`,而不是让 Agent 猜下一步。 +- 嵌套 Skill 调用只写 Skill 名字,不写 provider 前缀。 +- 对 `/comet-classic` 定制,保留 `open / design / build / verify / archive` 与 `.comet.yaml` 语义。 +- 对任意 Skill 组合,整理为 Comet-like 多 Node workflow。 + +禁止: + +- 复制粘贴原 Skill 全文。 +- 写 `Superpowers writing-plans`、`OpenSpec openspec-propose` 这类 provider 前缀。 +- 在中文 Skill 中混入英文流程句。 +- 把审计报告、source hash、内部 metadata 泄漏到用户可见 `SKILL.md`。 + +## 自检 + +返回前逐项检查: + +- 每个阶段都说明必须调用的 Skill、完成证据、脚本守卫和恢复入口。 +- 每个 `requiredSkillCalls` 都能在对应Node Skill 中找到明确加载指令和证据要求。 +- 自动推进引用脚本输出的 `NEXT:` 和 `SKILL:`。 +- Skill 调用只写 Skill 名字,不写 provider 前缀。 +- 中文用户可见文案没有混入英文流程句。 +- 没有复制粘贴原 Skill 全文。 + +## 必须返回的 claim + +- 每个 internal Node Skill 的 `node-skill:` + +缺少任一 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、 +claim 列表、未解决疑虑和建议返工点。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、 +尝试过什么、需要主会话如何处理。 diff --git a/.pi/skills/comet-any/reference/subagents/skill-reviewer.md b/.pi/skills/comet-any/reference/subagents/skill-reviewer.md new file mode 100644 index 0000000..56a7245 --- /dev/null +++ b/.pi/skills/comet-any/reference/subagents/skill-reviewer.md @@ -0,0 +1,126 @@ +# Skill 审查 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +审查其他 subagent 的 artifacts 和 claims,判断候选 Skill 是否已经像 Comet 一样可用。 +审查必须给出明确结论:`Review passed` 或 blocking findings。 + +必须覆盖: + +- `reference/skill-review.md` +- `reference/authoring-lanes.json` + +## 输入 + +读取主会话提供的通用输入,以及其他五个作者角色返回的全部 artifacts、claims 和 findings。 + +使用文件交接:主会话提供路径,不粘贴大段全文。读取总览、通用输入、五个作者报告文件、artifact 路径和 +claims。不得读取主会话历史来替代 artifact 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "审查 的 Comet-like Skill 产物" +model: <必须显式指定 model> +prompt: + 你是 Skill 审查 subagent。 + 先读取本 brief、通用输入路径、五个作者报告路径、artifact 路径、claim 清单和报告文件路径。 + 审查不信任作者报告;作者报告只是 claim,必须用 artifact 和 claim 交叉验证。 + 不要告诉审查者不要标记某问题,也不要预设某问题只能是 Minor。 + 审查是只读任务,不得修改工作树、索引、HEAD 或分支状态。 + 把完整审查写入报告文件路径,并返回两个 verdict。 +``` + +## 审查方法 + +审查不信任作者报告。先看 artifact 和 claim,再判断作者声称是否成立。不得用“作者说这是刻意设计” +来降低问题严重性。 + +审查必须给两个 verdict: + +- Skill 契约符合度:是否满足用户确认的目标、workflow protocol、`requiredSkillCalls`、claim、阶段推进、脚本守卫、停顿点和恢复要求。 +- 可用性质量:是否像 Comet 一样好用,是否清晰、可恢复、可审计、不会过度暴露内部 metadata。 + +证据必须引用 artifact 路径和 claim。不能只写“看起来可以”。 + +## 阻塞条件 + +出现以下任一情况必须给出 blocking findings: + +- 缺少 `reference/skill-review.md`。 +- 缺少 `reference/authoring-lanes.json`。 +- 缺少 workflow entry 作者、脚本作者、reference 作者、Skill 核心作者或停顿点作者的关键 claim。 +- entry Skill 把阶段路线写成多个 `**立即执行:**` Node Skill,而不是通过状态脚本只路由当前阶段。 +- 缺少 `workflow-state.mjs`、`workflow-guard.mjs` 或 `workflow-handoff.mjs` 契约。 +- 包内缺少六个生成脚本中的任何一个(`workflow-state`、`workflow-guard`、`workflow-handoff`、`comet-plan`、`comet-check`、`comet-hook-guard`)。 +- workflow entry 缺失,或 Skill 核心没有 internal Node Skill。 +- 阶段推进没有通过脚本输出 `NEXT:` 和 `SKILL:` 表达。 +- workflow protocol 声明的 `requiredSkillCalls` 没有在对应Node Skill 中明确要求加载,或 subagent 槽位没有要求子代理任务提示加载该 Skill。 +- 用户停顿点缺失,或停顿点可被默认值绕过。 +- 把确定性修复、guard 失败、状态对账、能力缺口、单一合法动作或 `NEXT: manual` 默认写成用户停顿点;或把可同时回答的相邻选择拆成连续确认。 +- entry Skill 的 frontmatter description 没有说明它是托管 workflow 的入口/恢复路由,或 internal Node Skill 的 description 允许普通任务直接触发而未限定为显式调用或 entry/runtime 路由。 +- 中文 Skill 混入英文流程句。 +- 嵌套 Skill 调用使用 provider 前缀。 +- 用户可见 `SKILL.md` 泄漏生成审计章节、source hash 或内部 metadata。 +- `/comet-classic` 定制替换或删除了 `open / design / build / verify / archive`、`.comet.yaml`、decision point、verify-result-transition 或 archive-delta-sync。 +- 任意 Skill 组合缺少自动推进、脚本守卫、用户停顿点、恢复或当前 draft hash 的 eval evidence。 + +## 严重级别 + +- Critical:会让生成 Skill 不可用、不可恢复、不可审计,或破坏 `/comet-classic` 受保护语义。 +- Important:会让阶段流程、脚本守卫、停顿点、Skill 调用或证据链不可信;必须修复后才能 ready。 +- Minor:不阻塞 ready 的清晰度、命名或维护性改进。 + +## 输出要求 + +返回: + +- `reference/skill-review.md` +- `reference/authoring-lanes.json` +- `review:skill-review` +- 最终 `Review passed` 或 blocking findings。 + +输出必须包含: + +- 两个 verdict:Skill 契约符合度、可用性质量。 +- Strengths:具体说明做得好的 artifact。 +- Issues:按 Critical、Important、Minor 分组。 +- 每个 finding 的 artifact 路径、claim、问题、影响和建议修复方式。 +- `Review passed` 或 blocking findings。 + +如果存在 Critical 或 Important,不得给出 `Review passed`。如果作者状态是 `BLOCKED` 或 `NEEDS_CONTEXT`, +必须返回 blocking findings,主会话必须补上下文、拆小任务、换更强模型或询问用户;不得继续组装。 + +## 多视角审查(按 depth) + +skill-review lane 是 DAG 的 barrier,在所有创作 lane 之后运行。其裁决用 `comet creator authoring-record --lane skill-review --file ` 记录,成为真实的 `skill-review.md` / `authoring-lanes.json` 证据——绝不是硬编码的批准。 + +- `depth: full`:派发 N=3 个独立审查者,每人只持一个 lens(`contract-fit`、`usability`、`evidence-trace`、`self-consistency`),互不见彼此结论。跨审查者的 findings 去重后,仅多数确认才成立。重复直到连续 `dryThreshold` 轮无新的 Critical/Important(上限 `maxRounds`)。`self-consistency` 必须交叉校验:引用的命令可解析、声明产物 == 实际产物、无幽灵命令引用(未注册的命令名)残留。 +- `depth: quick`:单审查者、单轮。 +- 仅当无 Critical/Important 残留时 `passed` 为 `true`。Minor 记录但不阻塞。 + +记录的 review 对象必须符合此形状(由 `authoring-record` 校验): + +```json +{ + "lane": "skill-review", + "status": "DONE", + "review": { + "passed": true, + "evidenceSource": "llm-multivote", + "voters": 3, + "lenses": ["contract-fit", "usability", "evidence-trace", "self-consistency"], + "rounds": 2, + "findings": [ + { "severity": "minor", "path": "SKILL.md", "problem": "措辞小问题。", "fix": "改写。" } + ], + "reviewedAt": "2026-06-28T00:00:00.000Z" + } +} +``` + +`passed: false` 的审查是 readiness blocker(不得 publish);缺失审查是 warning(包仍带诚实的 `deterministic-check-only` 占位,绝不伪造批准)。 diff --git a/.pi/skills/comet-any/reference/subagents/workflow-entry-author.md b/.pi/skills/comet-any/reference/subagents/workflow-entry-author.md new file mode 100644 index 0000000..9cab911 --- /dev/null +++ b/.pi/skills/comet-any/reference/subagents/workflow-entry-author.md @@ -0,0 +1,99 @@ +# Workflow Entry 作者 subagent + +本文件是 portable lane brief,不是 platform-native custom agent;如需 Claude Code custom agent,必须另行生成平台 agent 资源和 frontmatter。 + +## 职责 + +编写生成 Skill 的 entry `SKILL.md`。entry 只负责入口、恢复、主路由说明和用户停顿点;不得把阶段路线写成会立即触发多个 Skill 的执行清单。 + +必须覆盖: + +- entry Skill +- workflow-state / workflow-guard / workflow-handoff 的入口说明 +- `workflow-entry` claim + +## 创作区(你写什么) + +生成器把 entry SKILL.md 组装成确定性 **Auto 区**(frontmatter、Workflow Nodes 路由表、Skill Bindings、Guardrails And Evidence、Runtime And Recovery)+ 由你编写的 **Authored 区**(`## Decision Core`)。**你只写 Decision Core 正文,不写整个文件**。主会话通过 `comet creator authoring-record --lane workflow-entry --file ` 记录;artifact `SKILL.md` 的 `content` 即 Decision Core 正文。 + +质量标尺:`comet/SKILL.md` 的 Decision Core(完整 entry Decision Core 范例见 `reference/authored-zone-example.md`)。写 agent 可读的决策规则——机械路由已由 Auto 区的 `workflow-state.mjs next` 处理,所以聚焦判断: + +- **语义化当前节点检测** — 如何判断用户在哪个 Node,而非只跑脚本。建模 comet 的 Step 0(从用户消息检测意图,检查 Node 顺序,处理"属于前序/后序 Node"的冲突)+ Step 1(读状态,文件优先于过期状态)。 +- **Resume 与 drift 规则** — 上下文恢复时怎么办(从头重新检测,永远不信任对话历史),状态说 DONE 但 artifact 缺失时怎么办,用户在 Node 中途换话题时怎么办。 +- **决策分类与决策点** — 先区分用户决策、自动处理、停止条件和手动衔接,再只为真正需要用户选择的情况建表。清晰的首次调用、可确定修复的 guard 失败、单一合法下一步和 `NEXT: manual` 都不得制造确认点。 +- **Red flags** — "agent 想法 → 实际风险"模式,抓自欺(如"用户提到了主题所以研究已确认" → 提到 ≠ 确认)。 + +没有这四个子节的 Decision Core 是 stub,不是 Decision Core。entry 是每次调用最先读取的文件——它决定了 Skill 感觉"智能"还是"机械"。 + +Auto 区的 Node 路由表仅供参考——不要复制成执行清单,不要发出多个立即 Skill 加载。 + +## 输入 + +读取主会话提供的通用输入,尤其关注: + +- 用户确认的目标、语言和阶段名。 +- `reference/workflow-protocol.json` 的阶段顺序、插槽、`requiredSkillCalls` 和恢复路径。 +- 脚本作者返回的 `status`、`init`、`next`、`NEXT:`、`SKILL:` 和 guard 契约。 +- `/comet-classic` 定制场景下必须保留的 open / design / build / verify / archive 边界。 + +使用文件交接:主会话提供路径,不粘贴大段全文。不要继承主会话历史;只使用本 brief、通用输入、脚本契约和 reference 证据。 + +## 派发模板 + +主会话派发时使用当前平台的 subagent 机制,形状应包含: + +```text +description: "编写 的 workflow entry" +model: <必须显式指定 model> +prompt: + 你是 workflow entry 作者 subagent。 + 先读取本 brief、通用输入路径、脚本契约路径、workflow protocol 路径和报告文件路径。 + 开始前先分类用户决策、自动处理、停止条件和手动衔接;如果启动路由、恢复路径、当前阶段判定或真正的用户选择不清楚,先返回 NEEDS_CONTEXT。 + 不要猜测或自行补全缺失流程。 + 只写 entry SKILL.md 草稿,不写 internal Node Skill,不写 Bundle state,不执行候选脚本。 + Decision Core 必须包含四个子节:### 自动节点检测(Step 0 意图检测 + Step 1 状态读取 + Resume 规则)、### 决策分类与决策点(只列真正的用户选择)、### Red Flags(agent 想法 → 实际风险表格)。不得把 guard 失败、确定性修复、单一合法动作或手动衔接列为用户决策。没有这些子节的 Decision Core 是 stub。 + 把完整 entry 草稿写入报告文件路径,并只返回 15 行以内状态摘要。 +``` + +## 输出要求 + +entry 草稿必须体现: + +- 进入 Skill 后先读取 workflow 状态,不直接加载Node Skill。 +- 未启动时先初始化状态,再查询 `next`。 +- 只有脚本输出 `NEXT: auto` 和 `SKILL: ` 后,才加载这一个 Node Skill。 +- 阶段路线只能作为参考表,不能使用“立即执行”或“必须加载”这类执行指令。 +- 对 `/comet-classic` 定制,entry 必须列出必调槽位 Skill,但只能作为阶段内义务说明,不能变成 entry 立即执行清单。 +- 用户决策、自动处理、停止条件、手动衔接、恢复路径和参考文件清楚可见;只有至少两个真实合法选项时才停顿,相邻选择应合并。 +- 对 `/comet-classic` 定制,说明保留 open / design / build / verify / archive 主路径和阶段守卫。 + +禁止: + +- 在 entry `SKILL.md` 中写多个 `**立即执行:**` Node Skill。 +- 复制粘贴原 Skill 全文。 +- 写 provider 前缀。 +- 把审计报告、source hash、内部 metadata 泄漏到用户可见 `SKILL.md`。 + +## 自检 + +返回前逐项检查: + +- Decision Core 包含全部四个必须子节:自动节点检测、决策点、Red Flags、Error Handling 或 Resume Rules。 +- entry 只有一个主路由启动协议。 +- entry 没有立即加载Node Skill 的清单。 +- 阶段路线是参考,不是执行步骤。 +- 自动推进引用脚本输出的 `NEXT:` 和 `SKILL:`。 +- 清晰首次调用直接初始化;guard 失败先自动诊断或报告停止条件;`NEXT: manual` 只交还控制权;以上情况均不伪造用户决策。 +- 中文用户可见文案没有混入英文流程句。 + +## 必须返回的 claim + +- `workflow-entry` + +缺少该 claim 时,Skill 审查必须阻塞。 + +## 状态返回 + +状态必须是 `DONE`、`DONE_WITH_CONCERNS`、`NEEDS_CONTEXT`、`BLOCKED`。 + +完整报告写入报告文件路径。返回给主会话的摘要只返回 15 行以内状态摘要,包含状态、报告文件路径、claim 列表、未解决疑虑和建议返工点。若状态是 `BLOCKED` 或 `NEEDS_CONTEXT`,必须直接说明缺什么上下文、尝试过什么、需要主会话如何处理。 diff --git a/.pi/skills/comet-native/SKILL.md b/.pi/skills/comet-native/SKILL.md new file mode 100644 index 0000000..a584acb --- /dev/null +++ b/.pi/skills/comet-native/SKILL.md @@ -0,0 +1,265 @@ +--- +name: comet-native +description: 当用户明确调用 /comet-native、要求启动或恢复 Native change,或入口路由到 Native 时使用;负责澄清需求、读取状态并推动 Shape → Build → Verify → Archive。 +--- + +# Comet Native + +Native 保存需求、完整目标规格、状态和证据。你负责理解、实现和验证;Runtime 负责状态、边界和恢复。 + +整个流程在本 Skill 内完成。不要加载阶段 Skill,也不要强制套用 Plan、TDD、Debug 或 Review 方法。 + +## 需求澄清协议 + +从 `.comet/config.yaml` 读取 `native.clarification_mode`。允许值为 `sequential` 和 `batch`;字段缺失时使用 `sequential`。该配置决定用户问题的组织方式和离开 Shape 前的确认契约,不改变 Native 的阶段、change schema、安全确认或调用方停点。 + +先识别会改变用户可见结果、但尚未定义的分支。“规范化”“直观”“标准”“预期”等词不是产品契约;只有用户原话、用户确认的答案,或明确适用于当前行为的公开契约可以关闭这类分支。 + +仓库现状、依赖默认值、相邻功能和行业惯例只能支持推荐,不能代替用户决定。“保持现有行为”只约束已经存在的结果,不自动定义新行为。 + +先判断分支是否只影响实现方式,而不改变任何用户可见结果。不能证明这一点时,按用户决定处理。即使用户要求“不要询问实现选择”,也不能把产品决定重新归类为实现选择。 + +能从仓库、工具或运行环境查明的事实由你负责调查,不交给用户回答。宿主支持并行工作时,可以并行调查相互独立的事实,但不能把并行能力作为流程前提。尚未查清的事实只阻塞依赖它的问题,不阻塞其他已经具备条件的问题。 + +只有共同定义同一个用户决定的多个细节才合并为一道策略问题。彼此独立的用户决定不能合并:Sequential 模式逐轮处理,Batch 模式为每项分别编号。不要为了增加问题数量制造歧义,也不要把实现选择放入用户问题清单。若一道问题仍未覆盖同一决定的某种合理解释,应拓宽这道问题,而不是另造一个依赖关系不清的问题。 + +### 提问载体 + +提问前先检查当前宿主的工具列表。当前工具列表提供 `AskUserQuestion` 时,Claude Code 下优先使用它展示结构化选项;其他宿主使用等价的用户输入工具。每个选项都要有简短标签和影响说明,推荐项在说明中标明,但不能自动代用户选中。 + +- Sequential 模式每轮提交一道结构化问题。选项互斥时使用单选;只有同一个用户决定本身允许同时选择多个彼此兼容的选项时,才使用多选。不能用一道多选题压缩多个独立的用户决定。 +- Batch 模式在整组问题都能满足当前工具的问题数、选项数和字段限制时,把本轮整组问题放在同一次调用中。不能把同一轮拆成多次工具调用,让后面的问题在用户回答前无法同时展示。 +- 当前宿主没有结构化提问工具,或 Batch 整轮无法在一次调用中完整表达时,整轮使用编号文本降级模式。文本必须保留相同的问题、选项、推荐和影响,然后停止并等待用户回复编号。 +- 若第一次调用失败或宿主报错,判定本会话结构化提问不可用,当轮改用文本降级模式,本会话后续不再重试。工具调用成功后直接等待用户作答,不再同时输出一套重复的文本问题。 + +### Sequential 模式 + +把当前目标视为一棵用户可见结果的决策树。进入 Shape、用户每次回答后,以及相关仓库事实发生变化后,都必须重新执行以下循环: + +1. 先调查当前分支依赖的仓库、工具和运行环境事实,不把可自行查明的事实问给用户。 +2. 从目标向下逐一遍历所有合理可达的用户可见分支,包括当前答案会引出的下游边界、失败结果和默认行为。不能在得到第一个可行解释后停止,也不能用推荐项静默关闭未确认分支。 +3. 在正式产物中维护可复核的未决事项及必要依赖摘要,不保存隐藏推理或完整内部推演。若仍有多个独立决定,选择前置条件已满足且最上游的一个;其他决定留到后续轮次。 +4. 在 brief 中只记录当前一道 `- [blocking] <问题>`。一次只问最上游的一个问题:每轮只询问这一个用户决定,不把多个独立决定塞进同一道多选题或多个并列问句。 +5. 给出“问题 / 推荐 / 影响”,用选项、正例或反例让差异可判断,然后结束本轮。推荐必须明确,但不能代用户选择。新答案引出的每一道下游或依赖问题也必须完整保留这三部分,不能只给一个候选集合后要求用户增删。 + +用户回答后,立即把确认内容写入 Decisions 和完整目标规格,移除当前阻塞项,再从目标开始重新遍历整棵决策树。目标规格尚不存在时,必须在处理该答案的同一轮创建;只更新 brief、等到最终确认或 Build 才补规格,都不算完成答案持久化。把答案当作产品约束,不当作获准采用的实现算法:一轮答案只能关闭该问题明确选择的输入→输出结果,不能因为某种实现会顺带处理其他反例,就把独立的空输入、失败或嵌入边界静默视为已决定。新答案引出的分支必须进入后续轮次;模糊、部分或未回答的内容继续保持 `[blocking]`。 + +当没有未决用户决定时,仍不能直接进入 Build。先进行一次完整性复核,主动寻找被遗漏或静默假设的用户可见分支;然后向用户给出包含目标、范围、关键决定、验收标准和明确非目标的共享理解摘要,并在 brief 中记录 `- [blocking] CONFIRM: <确认内容>`。这次复核也是可追溯性检查:摘要和验收示例中的每一项产品行为都必须能追溯到用户原话、某轮已确认答案或明确适用的公开契约;“一致”“直观”“通常如此”和仓库惯例不算确认。若某项策略第一次出现在摘要中,说明澄清循环尚未完成,必须把它重新标为用户问题,而不能借最终确认一次性决定。用户明确确认前,不修改项目实现、不进入 Build、也不调用 `next`。用户补充或否定时,更新正式产物并重新执行上述循环;明确确认后移除该阻塞项,记录确认,并使用 `--confirmed` 推进。 + +### Batch 模式 + +把当前目标视为一棵用户可见结果的决策树,并把尚未确定的用户决定按前置关系组织起来。只需要维护可复核的未决事项、依赖摘要和正式产物,不保存隐藏推理或完整内部推演。 + +每轮计算“本轮可回答问题集”:其中每个问题的前置决定都已确定,所需环境事实也已查清,并且答案不依赖本轮其他问题。这些问题组成当前 frontier;依赖仍未确定决定或调查中事实的问题留到后续轮次。 + +某个候选分支需要环境事实时,事实调查仍由你负责。宿主支持 sub-agent 或其他并行工作时,必须并行启动相互独立的事实调查;调查中的事实只暂缓依赖它的下游问题,其他 frontier 问题必须在当前轮继续提出。宿主不支持并行时,由当前执行者直接调查,不能把并行能力作为流程前提,也不能把可查明的事实转交用户。 + +对本轮可回答问题集执行以下步骤: + +1. 在 brief 的 Open questions 中使用 `- [blocking] Q1: <问题>`、`- [blocking] Q2: <问题>` 的固定格式分别保存各题;不要使用 Markdown 有序列表代替该前缀。 +2. 一次提出整组问题,并为每题给出“问题 / 推荐 / 影响”。编号应允许用户用“1 按推荐,2 选择 B”这类方式回答。 +3. 更新正式产物并提出问题后结束本轮,不进入 Build,也不调用 `next`。 + +使用以下格式: + +```text +1. 问题:…… + 推荐:…… + 影响:…… + +2. 问题:…… + 推荐:…… + 影响:…… +``` + +用户回答后,把已确认内容写入 Decisions 和完整目标规格,移除对应 `[blocking]`;没有回答或回答不明确的问题继续保持 `[blocking]`,不得按推荐项自行补全。随后根据新答案重新计算本轮可回答问题集,逐轮处理新出现的分支。 + +当本轮可回答问题集为空、相关事实已经查清且所有已识别用户决定均已处理时,执行一次完整性复核,重新检查是否仍有未处理或被静默假设的用户可见分支。向用户给出包含目标、范围、关键决定、验收标准和明确非目标的共享理解摘要,并在 brief 中使用 `- [blocking] CONFIRM: <确认内容>` 记录最终确认。用户明确确认前,不进入 Build,也不调用 `next`;用户补充或否定时,更新相应分支并继续下一轮。明确确认后,移除该阻塞项、记录确认,并使用 `--confirmed` 推进。 + +以文本“规范化”为例,一个完整问题应同时说明大小写折叠、外围标点、内部标点或撇号保留,并用反例展示不同选择的输出。 + +达成共享理解前,可以调查仓库事实、创建或恢复 Native change,并在 brief 中记录 `[blocking]`。不要进入 Build、修改项目实现或调用 `next`。 + +用户回答后,更新原有 change 的 brief 和完整目标规格,再重新检查是否还有用户决定。不要为补充答案创建第二个 change,也不要把未确认选项写成既定规格。 + +离开 Shape 时,只有本轮刚记录了用户对既有阻塞问题的确认,才给 `next` 传 `--confirmed`。Batch 模式的最终共享理解确认属于这类确认;用户最初提出需求不算。 + +若调用方要求在该 transition 后停下或切换会话,严格执行以下序列:更新正式产物 → 调用一次允许的 transition → transition 成功后不再调用工具 → 输出约定标记并结束本轮。即使 Runtime 返回 `continuation.disposition: continue`,也不能越过这个停点。 + +## 执行边界与状态快照 + +调用方指定停点时,只完成停点前允许的工作。下一会话重新调用 `/comet-native`,从磁盘读取 status、selection 和正式产物后继续,不依赖聊天记忆恢复进度。 + +调用方若要求保留某次状态变化前的 Runtime 返回快照,应在执行该状态变化之前,使用真实命令的机器可读模式,并通过重定向直接保存标准输出。快照确认完整后不得重建、刷新或覆盖;它只反映生成时的真实状态,不能根据最终结果补写。 + +## 开始或恢复 + +`/comet-native` 是 Skill 入口,不是 shell 命令。通过宿主的 Skill 机制调用;不要在 shell 中执行 `/comet-native`。 + +先运行 Native `status` 和 `show`。恢复 Verify 或 Archive 时,运行 `status --details`,读取有预算的验收页、详细 findings、`findingsTruncated` 和最新 checkpoint。 + +- findings 被截断时,先处理已返回项,再重新读取 details。 +- `acceptancePage.nextCursor` 非空时,按命令参考继续分页。 +- 随后读取 `.comet/config.yaml`,确定 `native.clarification_mode`,再读取 `comet-state.yaml`、brief、拟议规格、canonical 规格、仓库实现、项目规则和相关测试。 +- 磁盘与仓库事实优先于聊天记忆;能从环境取得的事实不要询问用户。 + +已有 active change 时,先只读确认哪个 change 对应当前目标。确认后显式运行: + +```text +comet native select +``` + +该命令建立项目级共享 selection。不要新增 `resume` 命令,也不要依赖 `status` 或 `show` 的读取副作用建立归属。 + +存在多个 active change,且 selection 不能唯一确定目标时,让用户选择。只有磁盘事实证明没有 active change 时,才把目标归纳为 lowercase kebab-case 名称并创建: + +```text +comet native new --language zh-CN +``` + +只使用配置指定的 `/comet/`,不扫描或修改其他工作流目录。 + +命令与 Runtime 定位见[命令参考](reference/commands.md),产物格式见[产物参考](reference/artifacts.md),中断与恢复见[恢复参考](reference/recovery.md)。自带 Runtime 位于 [scripts/comet-native-runtime.mjs](scripts/comet-native-runtime.mjs)。 + +项目只安装一份 Comet 工作流 Rule;支持 Hook 的平台只安装一个 `comet-hook-router.mjs`。Rule 与 Router 根据 `.comet/config.yaml` 和 `.comet/current-change.json` 确定当前 workflow,一次写入最多路由给一个 Guard。 + +当前 change 属于 Native 时,只应用 Native 的 Shape、Build、Verify、Archive 边界。不要同时运行 Native 与 Classic Guard,也不要用默认 workflow 猜测当前 change 的归属。Native 主流程不依赖任何外部 Skill。 + +## 决策协议 + +维护一份未决事项清单,按依赖顺序处理仍没有唯一答案的用户可见分支。重点检查: + +- 输出与默认行为; +- 边界条件与失败结果; +- 范围、风险和不可逆操作; +- 明确适用于当前行为的已有约束。 + +把关键名词或动作改写成可区分解释的“输入 → 输出”或“触发 → 结果”示例。一个反例能区分两种合理解释,就说明仍需用户决定。 + +文本或 token 行为通常要检查大小写、首尾与内部标点、空白、Unicode、空输入、重复项、顺序和并列结果。CLI 或 API 行为通常要检查默认值和错误结果。不要为了覆盖清单制造不存在的歧义。 + +对解析、计数、分词或边界识别,完整性复核还要用反例检查:空输入与纯空白、没有分隔符、连续或混合分隔符,以及看似分隔符但属于有效内容的字符(例如缩写中的句点、数字中的小数点或转义后的分隔符)。只有任务范围内的反例才需要询问;明确非目标应直接排除。若两个反例可以独立选择不同结果,就不能合并为一个用户决定。契约只覆盖“非空输入”时,不能把它当作空输入行为的依据。 + +只有用户给出的信息、明确非目标、已确认决定,或当前能力的明确公开契约可以消除分支。发现阻塞后,按需求澄清协议为当前模式计算并提出一个问题或本轮可回答问题集;回答前不要调用 `next` 或修改项目实现。 + +当未决事项清单为空,且仅凭 brief、完整目标规格、仓库事实和项目规则就能实现并验收,两种模式都先完成最终共享理解确认;Sequential 每轮仍只提出一个用户决定,Batch 仍按本轮可回答问题集成组提出。 + +## 推进契约 + +Shape、Build、Verify 的 transition 会返回 `next: auto | manual`,以及 `continuation.disposition: continue | await-user | blocked | done`、所需输入和下一动作。Archive 不通过 `next` 推进;成功归档才返回 `done`。 + +这些字段组成机器可读的 continuation 契约。`next: auto` 只表示当前 transition 已成功,不代表宿主会在后台执行后续工作。 + +收到 `next: auto` 且 disposition 为 `continue` 后,重新读取返回的 phase 和必要产物。没有用户决定或 Runtime 阻塞时,在本 Skill 内持续推进下一阶段,不等待用户再次触发。 + +若 disposition 为 `await-user`、`blocked` 或 `next: manual`,先根据磁盘事实和 blocking findings 处理。只有缺少的输入确实属于用户决定时才提问。 + +Sequential 模式的当前问题和最终共享理解确认,以及 Batch 模式中尚未回答的问题和最终共享理解确认,都保持为 `[blocking]`。它们是需要等待用户输入的正常停点,不能通过自动推进绕过。 + +`workspace-root-changed` 与 `workspace-inspection-unavailable` 是只读提示,不单独阻止推进或归档。未知 workspace finding、确定冲突、失效证据和 repair stop 必须处理。 + +长任务需要保留阶段内进度时,使用 `comet native checkpoint` 保存简短摘要、下一动作和真实产物引用。checkpoint 不推进 phase,也不替代 brief、规格或验证报告;不要另建 resume、handoff 或任务清单。 + +## Shape + +确认并写入:Outcome、Scope、Non-goals、Acceptance examples、Constraints and invariants、Decisions、Open questions、Verification expectations。阻塞问题在 brief 中标记为 `- [blocking]`;Batch 模式可以同时保存本轮全部可回答问题。 + +只有当 brief、完整目标规格、仓库事实和项目规则足以让后续执行者在不猜测用户可见行为的情况下实现并验收,Shape 才算完成。 + +- 更新 `brief.md`,使其能够约束实现与验收。 +- 用户明确给出的 lowercase kebab-case capability ID 必须原样用于 `specs//spec.md`。 +- 用户只给出显示名称时,在正文保留原名,并稳定派生 lowercase kebab-case capability ID。 +- 长期行为发生变化时,写归档后的完整目标规格,不写增量 patch。 +- 删除 capability 时运行 `comet native spec remove `;operation 和 canonical base hash 由 Runtime 推断并冻结。 +- 仍有未决事项时保留 `[blocking]` 并停下。 + +准备完成后运行: + +```text +comet native next --summary <摘要> [--confirmed] +``` + +仅在本轮刚记录用户对既有阻塞问题的确认时追加 `--confirmed`;Sequential 和 Batch 模式都必须先取得最终共享理解确认。Runtime 会把 approval 绑定到当时的 brief/spec contract hash;若 Build 中 contract 发生变化,先取得用户对当前 contract 的确认,再按 status 返回的命令重试。不要手工编辑 `approval` 或 `approved_contract_hash`。 + +## Build + +选择满足 brief 与拟议规格的最简单可靠方案。实现方式、是否保存计划、测试粒度、调试方法和审查强度由你根据风险决定。 + +不要为流程制造额外文档。发现需求或规格漂移时,先更新 Native 产物;出现新的用户决定时标记 `[blocking]`,按当前配置的澄清协议处理。Sequential 模式需要重新遍历决策树,Batch 模式需要重新计算问题集;两种模式都要在继续实现前取得更新后共享理解的最终确认。 + +完成后提供真实项目产物;没有代码变化时给出明确理由。然后运行: + +```text +comet native next --summary <摘要> --artifact <项目内路径> [--confirmed] +``` + +没有代码变化时按命令参考使用 `--no-code-reason`。Runtime 会返回 implementation scope 和首个 `acceptancePage`;保存 Runtime 派生的验收 ID,并按 `nextCursor` 读取全部页面。不要自行计算 ID。 + +Git 快照只包含 tracked 和未被 ignore 的 untracked 文件,submodule/gitlink 作为原子条目。非 Git 项目使用有界物理树快照。 + +- `git-selection-changed`:等待 Git 写入稳定后重试,不能授权为 partial scope。 +- `git-enumeration-limit`:先缩小或清理项目所有范围;只有 Runtime 返回可授权 scope,且用户接受未枚举尾部的具体风险时,才能使用 partial 协议。 +- `physical-selection-changed` 或 `physical-enumeration-limit`:等待文件系统稳定或缩小项目树后重试,不能授权为 partial scope。 + +Runtime 无法证明 scope 完整时会停在 Build,返回 partial scope hash 和未归属项。先补充真实 artifact 或消除未归属变化。确实只能接受 partial 时,说明具体缺口并取得用户确认,再使用同一个 hash: + +```text +--allow-partial-scope --partial-reason <理由> --confirmed +``` + +不要手改 snapshot、evidence 或猜测未枚举路径,也不要把 partial 写成 complete。 + +## Verify + +根据 Acceptance examples、完整目标规格和风险运行验证。记录实际命令、结果、跳过项、规格一致性、已知限制和结论;未运行的检查不能写成通过。 + +在 `verification.md` 的固定 acceptance evidence 块中逐项使用 Runtime 返回的 `acceptance_id`。每项只能记录项目相对 evidence refs,或记录诚实的 `skipped_reason`。用 `comet native evidence format` 把条目序列化成规范文本再粘贴,不要手工排版这段 JSON——手写几乎不可能逐字节匹配规范序列化规则。格式见产物参考。 + +需要一份可重建的文本卫生证据时,可运行内置只读检查: + +```text +comet native check +``` + +该命令只扫描当前 implementation scope/current snapshot 中有界的项目内普通文本文件,不调用 Git、shell、项目脚本、外部进程或外部 Skill。它不会修改项目文件、phase、Run 或 trajectory;结果写入内容寻址 receipt。它不替代按风险选择的项目测试。 + +写完报告后运行: + +```text +comet native next --summary <摘要> --result pass|fail --report verification.md [--receipt ] +``` + +fail 会回到 Build。修复后重新验证,并用 `--failure-category` 与 `--failed-check` 提交稳定、非敏感的失败事实。 + +同一失败第二次出现会告警;第三次且 scope 没有进展会停止。scope 发生真实变化会结束当前 repair episode。scope 未变化但有明确新假设时,可按 status 返回的 signature 使用一次 `--override-repair`;同一 signature 不得重复 override。达到停止条件后请用户决定,不要弱化检查或伪造 pass。 + +进入 Archive 后,brief、规格、implementation scope、报告或 receipt 发生变化会使证据失效。按 Runtime continuation 回到 Build,重新封印 scope 并验证;不要沿用失效的 pass。 + +## Archive + +状态进入 Archive 且 Verify 为 pass 后,先预演: + +```text +comet native archive --dry-run +``` + +检查 create/replace/remove、证据新鲜度、当前 Native root 内的 change 重叠和恢复状态。没有阻塞时,用本次预演返回的精确 hash 提交: + +```text +comet native archive --expect-preflight +``` + +调用方要求保存预演或提交 envelope 时,首次调用本身就使用机器可读模式并写入目标文件。提交使用已保存预演中的 hash;文件验证成功后保持不可变,不得在归档后重跑命令覆盖。 + +Runtime 会在锁内重新计算事实,发生漂移就拒绝提交。成功后更新 canonical 规格,并把 change 移入日期前缀的 archive 目录。 + +遇到 canonical 冲突时,重读并改写完整目标规格,再运行 `comet native spec rebase --summary <摘要>`。该命令会受控回到 Build;随后重新实现、确认、验证和归档。未完成事务按恢复参考处理。 + +## 不变规则 + +- 不直接编辑 `phase`、`approval`、`spec_changes`、Run state、trajectory、锁或 transaction journal。 +- 不跳过阶段检查。Shape、Build、Verify 使用 `comet native next`;Archive 使用两步预演与提交。 +- 不调用外部 Skill。Native 主流程只依赖 Comet 自带 Runtime。 +- 不保存隐藏推理,只保存摘要、产物引用、命令结果、hash、状态变化和时间戳。 +- 不把 token、密码、私钥、连接串或其他凭据写入摘要、理由与报告。 +- 没有用户决定或 Runtime 阻塞时持续推进;有用户决定时,Sequential 模式只问一个最上游问题,Batch 模式询问本轮全部可回答问题,随后等待回答。 diff --git a/.pi/skills/comet-native/reference/artifacts.md b/.pi/skills/comet-native/reference/artifacts.md new file mode 100644 index 0000000..26167e6 --- /dev/null +++ b/.pi/skills/comet-native/reference/artifacts.md @@ -0,0 +1,221 @@ +# Native 产物参考 + +## 目录 + +```text +/.comet/config.yaml +/.comet/current-change.json # Native/Classic 共享的当前需求归属 +/comet/ + specs//spec.md + changes// + comet-state.yaml + brief.md + specs/ + verification.md + runtime/ + baseline-manifest.json + workspace.json # process-free 物理 root 身份;只作提示 + run-state.json # 通过 Native Protected Run I/O 读写 + trajectory.jsonl # 有事件数、单事件和总字节预算 + pending-action.json # 可选;仅在 Run 有待处理动作时存在 + context.md # 可选;Run context ref + artifacts.json # 可选;Run artifact refs + transition.json # 可选;未完成阶段推进日志 + checkpoint-journal.json # 可选;未完成 progress checkpoint 日志 + checkpoints/ + latest.json # 最近阶段边界 checkpoint + progress.json + manifests/.json + evidence/ + scopes/.json + snapshots/.json + allowances/.json + verifications/.json + check-receipts/.json + archive/YYYY-MM-DD-/ + runtime/ + locks/ + transactions// + transaction.json + events.jsonl +``` + +`artifact-root` 由项目配置唯一指定。Native 不使用隐藏的 change 目录,也不从其他需求目录发现状态。项目级 `.comet/config.yaml` 是持久配置例外;`root move` 中断恢复期间还可能在源/目标 artifact root 旁出现 Runtime 管理的临时 staging 或 quarantine,事务收口后会清除。它们不是第二个可写 Native change 根。 + +## 项目配置 + +```yaml +schema: comet.project.v1 +default_workflow: native +workflows: + - native +native: + artifact_root: docs + language: zh-CN + clarification_mode: sequential + snapshot: + include: + - "**/*" + exclude: [] + max_files: 10000 + max_total_bytes: 268435456 + max_duration_ms: 60000 +``` + +`clarification_mode` 控制 Native 如何组织用户决定,以及离开 Shape 前采用哪条确认契约:`sequential` 每轮询问一个最上游问题,`batch` 每轮询问所有前置条件已经确定的问题。字段缺失时使用 `sequential`。它不改变 change schema、生命周期、安全确认或调用方停点。 + +`snapshot` 定义内容快照的显式范围与资源预算。`include`/`exclude` 使用项目相对 `/` 路径以及 `*`、`**`、`?`;规范化策略及 hash 会写入新 change 的 baseline。后续 current snapshot 继续使用 baseline 策略,不能靠中途修改范围隐藏实现变化。`max_files`、`max_total_bytes` 和 `max_duration_ms` 只限制捕获工作,可按仓库规模提高;文件内容使用流式 SHA-256,不依赖 Git object hash,也没有独立的 5 MiB 单文件限制。 + +根目录迁移期间会出现 runtime 管理的 `pending_root_move`。存在该字段时普通写命令必须停止,不能自行选择旧根或新根。 + +## 当前需求归属 + +```json +{ + "schema": "comet.selection.v2", + "workflow": "native", + "change": "add-sentence-counting", + "branch": null +} +``` + +`.comet/current-change.json` 是 Native 与 Classic 共用的当前需求 selection,不是 Native change 产物。Native 的 `new` 和 `select` 会写入 `workflow: native`;Hook Router 每次只把写入交给这一个 workflow 的 Guard。selection 缺失时只有全项目恰好一个 active Comet change 才能只读推断;多个候选、失效 selection 或已归档目标都会失败关闭。 + +项目配置最多 64 KiB,selection 最多 16 KiB,change YAML 最多 256 KiB。brief 与单个拟议规格最多各 1 MiB;一个 change 最多 64 个拟议规格,contract 的 brief + specs 总读取最多 4 MiB。拟议规格目录还限制可枚举条目,`show` 的序列化载荷最多 10 MiB。超过预算时 Runtime 保留原文件并失败关闭,不以静默截断替代完整需求。 + +## Change 状态 + +```yaml +schema: comet.native.v3 +minimum_runtime_version: 3 +revision: 1 +name: add-sentence-counting +language: zh-CN +phase: shape +brief: brief.md +approval: null +approved_contract_hash: null +spec_changes: + - capability: sentence-counting + operation: create + source: specs/sentence-counting/spec.md + base_hash: null +verification_result: pending +verification_report: null +implementation_scope: null +verification_evidence: null +partial_allowance: null +archived: false +created_at: 2026-07-14 +run_id: null +``` + +不要直接编辑 Runtime 管理字段。`phase`、`revision`、`approval`、`approved_contract_hash`、`spec_changes`、operation、`base_hash`、三个 evidence ref、`run_id` 和 `archived` 都由 Runtime 管理。 + +`approval: confirmed` 表示 Runtime 已记录用户对当前共享理解的明确确认。`implicit` 只用于兼容旧 change,不代表用户已经确认;处于 Build 的旧 `implicit` change 必须确认后才能进入 Verify。`approved_contract_hash` 把 approval 绑定到当时的 brief/spec contract,contract 发生变化后也必须由用户重新确认。需要改变需求时,只更新 brief 和 `specs//spec.md`;删除 capability 使用 `comet native spec remove`,再由命令检查并推进。 + +## Brief + +`brief.md` 固定使用八个一级标题: + +```text +# Outcome +# Scope +# Non-goals +# Acceptance examples +# Constraints and invariants +# Decisions +# Open questions +# Verification expectations +``` + +前四节必须有实质内容。仍阻塞实现的问题在 Open questions 下以 `- [blocking]` 开头;普通备注不会阻塞 Shape。 + +Sequential 模式的 Open questions 同时保存一个最上游阻塞问题。Batch 模式使用 `- [blocking] Q1: <问题>`、`- [blocking] Q2: <问题>` 保存本轮全部可回答问题;该无序列表前缀是 Runtime 识别阻塞的固定格式,不能改成 Markdown 有序列表。未回答项继续保持 `[blocking]`。当前模式的全部问题处理完且完整性复核通过后,两种模式都使用 `- [blocking] CONFIRM: <确认内容>` 保存共享理解确认,明确确认前不能进入 Build。 + +问题编号只服务于当前澄清轮次。已确认答案应写入 Decisions 和完整目标规格;不要新增决策树产物,也不要把隐藏推理写入 brief。 + +## 完整目标规格 + +拟议规格固定写在 `changes//specs//spec.md`,描述归档后 capability 应有的完整行为,不写只在旧文本上成立的增量片段。每个 capability 只能出现一次操作: + +| operation | canonical 现状 | source | base_hash | +| --- | --- | --- | --- | +| `create` | 必须不存在 | 必填 | `null` | +| `replace` | 必须存在 | 必填 | 当前 canonical 文件 SHA-256 | +| `remove` | 必须存在 | 禁止 | 当前 canonical 文件 SHA-256 | + +`next` 首次发现 proposed spec 时推断 create/replace 并冻结 hash;`spec remove` 为 remove 冻结 hash。归档在锁内重新计算 hash,实际值与 `base_hash` 不同表示并发变化,必须重新读取并改写完整目标规格,再用 `spec rebase` 受控刷新基线、回到 Build 并重新验证,不能覆盖或手改 hash。 + +## Verification + +`verification.md` 固定使用六个非空一级标题: + +```text +# Acceptance evidence +# Commands and results +# Skipped checks +# Spec consistency +# Known limitations and risks +# Conclusion +``` + +保存可复核事实,不保存隐藏推理文本。未运行的检查放在 Skipped checks,失败结果不能写成 pass。 + +Runtime 最多从 brief 与拟议规格合计派生 1024 个验收项,超出就拒绝继续,不会先构造无界列表再截断。`acceptancePage` 每页最多 16 项;单项文字最多 512 UTF-8 字节、context 最多 4 项且每项最多 256 字节,整页最多 32 KiB。文字或 context 截断会显式标记,验收 ID 不会因分页或截断而丢失;cursor 绑定当前 acceptance hash,契约变化后旧 cursor 会失效。 + +`# Acceptance evidence` 下必须恰好有一个固定机器块。ID 由 Runtime 从 brief/spec 派生,通过 Build 结果或 `status --details` 返回;不要自行计算或改写。生成这个块时用 `comet native evidence format` 把条目序列化成规范文本再粘贴进去,不要手工排版 JSON——手写几乎不可能逐字节匹配规范序列化规则,会被拒绝并报 "canonical serialization" 错误: + +```text + +[ + { + "acceptance_id": "acceptance-", + "evidence_refs": [ + "src/feature.ts" + ] + }, + { + "acceptance_id": "acceptance-", + "evidence_refs": [], + "skipped_reason": "该平台当前不可用。" + } +] + +``` + +数组按 `acceptance_id` 排序,`evidence_refs` 也排序。每项只能二选一:至少一个项目相对 evidence ref,或空数组加非空 `skipped_reason`。不能同时给证据和跳过理由,也不能引用绝对路径、Native 外部路径、`.git` 或 `.env*`。 + +```text +comet native evidence format [--entries ] +``` + +把上面这份条目数组(不带 markers)以 JSON 通过 stdin 传入,或用 `--entries ` 指定一个 JSON 文件;命令输出的文本已经包含 start/end markers 和规范排序、缩进,原样粘贴到 verification.md 的 `# Acceptance evidence` 一节即可。 + +## 内容寻址证据 + +- `baseline-manifest.json`:change 创建时的有界项目快照。它只记录项目相对路径、size、内容 hash、capture provider、规范化 scope policy、实际资源预算和省略事实,不保存文件内容。Git 只用于项目文件枚举和 gitlink 边界,不作为普通文件的内容身份;普通文件始终流式计算 SHA-256。Git provider 纳入 tracked 和未被 ignore 的 untracked 文件,并把 submodule/gitlink 作为原子条目;非 Git 项目使用带前后枚举围栏的有界物理树 provider。显式排除项属于 baseline 定义之外,不伪装成 omission;创建时若策略范围内仍有省略项,`new` 会失败并清理未完成 change,同时返回实际限制与支持的配置修复路径。 + - `git-selection-changed`:等待 Git 写入稳定后重试,不能授权为 partial scope。 + - `git-enumeration-limit`:先缩小或清理项目所有范围。只有 current snapshot 返回可授权 scope,且用户接受未知尾部的具体风险时,才能按精确 hash、理由与 `--confirmed` 使用 partial 协议。 + - `physical-selection-changed` 和 `physical-enumeration-limit`:稳定或缩小项目树后重试,不能授权为 partial scope。 + - 任何情况都不能手改 evidence 或猜测未枚举路径。 +- `evidence/scopes/`:Build 离开时由 baseline、当前快照、声明产物和 contract 派生的 implementation scope。当前快照不完整时不会猜测删除;变化过多时只展开有界明细,其余由带数量与内容 hash 的 `scope-detail-overflow` 表示。scope 不完整时 Runtime 停止;用户显式接受后才生成 `allowances/`。 +- `evidence/verifications/`:Verify 结论的 envelope,绑定 Runtime 身份、change revision、contract、acceptance coverage、scope、报告 hash 和可选 check receipt。任一绑定事实变化都会 stale。 +- `evidence/check-receipts/`:`comet native check` 的内置策略结果。它只保存 policy/version、scope/snapshot 绑定、有界 issue 与计数,不保存文件内容,也不是测试完整性的证明。 +- `checkpoints/`:阶段内恢复摘要与真实产物 manifest。checkpoint 会递增 revision,但不改变 phase,也不能代替 brief、规格、scope 或 verification。 + +所有 hash ref 都由 Runtime 写入并在读取时重算。不要复制旧 ref 到新状态、手改 JSON 或把 receipt 当作 pass;`next`、status 和 Archive 会重新读取并检查新鲜度。 + +Evidence retention 只能由 doctor 显式执行,不在普通工作流中后台删除文件。只读 doctor 报告候选;`doctor --repair` 只清理 active change 中至少 30 天、每种 evidence kind 最新 32 份之外,并且能从当前 state refs 及其依赖闭包证明未引用的 snapshot、scope、allowance、verification 和 check receipt。 + +候选按 dependents-before-dependencies 排序。每个文件先在父链与身份复核后改名到同目录唯一 `.gc` quarantine,再复核并删除。中断的 quarantine 会由后续 doctor 发现;显式 repair 仅在原路径不存在且内容与身份有效时无覆盖恢复。 + +出现原文件与 quarantine 冲突、多份 quarantine、归档 change、pending transition/checkpoint、缺失依赖、损坏文档、未知目录项、symlink 或其他特殊文件时,推迟或拒绝清理。不要把 retention 当作修复损坏证据的办法。 + +Build 与 Verify 使用 inspect-then-persist:先计算并校验 contract、scope、acceptance、repair、Run 与 trajectory,再把最终证据引用写入状态和 transition。Partial Build 可先内容寻址保存候选 scope 以返回稳定 hash,但没有确认时不会生成 allowance 或推进;被后续检查阻塞的 Verify 不会留下可被误认成已提交结论的 verification evidence。 + +Run state、trajectory、checkpoint、pending action、context 和 artifact refs 只能经过 Native Protected Run I/O。读取拒绝 symlink/junction、非普通文件、路径或文件身份变化和越界,并执行打开前后复核;写入在原子提交前复核父链和目标身份。 + +当前预算为:Run state 256 KiB;trajectory 8 MiB、4096 个事件、单事件 256 KiB;checkpoint 与 pending action 各 256 KiB;context 与 artifact refs 各 1 MiB。通用 Engine 存储函数不是 Native 文件边界。 + +阶段 transition journal 最多 512 KiB,baseline manifest 的硬上限为 8 MiB。Archive/root move transaction journal 最多 256 KiB;`events.jsonl` 最多 1 MiB 或 1024 个事件,单个事件最多 16 KiB。摘要、无代码理由、partial 理由、repair override 摘要与跳过理由在持久化前执行长度校验和凭据形态脱敏;不要把 token、密码、私钥或连接串写入 workflow 证据。 diff --git a/.pi/skills/comet-native/reference/commands.md b/.pi/skills/comet-native/reference/commands.md new file mode 100644 index 0000000..2f31f2b --- /dev/null +++ b/.pi/skills/comet-native/reference/commands.md @@ -0,0 +1,124 @@ +# Native 命令参考 + +优先使用已安装的 `comet native`。若宿主环境只提供 Skill 文件,使用本 Skill 的自带 runtime: + +```text +node /scripts/comet-native-runtime.mjs [options] +``` + +两种入口的参数、stdout、stderr 和退出码相同。普通发现从当前目录向上寻找 `.comet/config.yaml` 或仓库根;生成式 launcher 可附加隐藏参数 `--project-root `。 + +## 项目与根目录 + +```text +comet native init [--root ] [--language en|zh-CN] +comet native root show +comet native root move +``` + +`artifact-root` 必须是项目内相对路径,默认值是 `docs`。`.` 生成 `/comet/`,`docs` 生成 `/docs/comet/`。`init --language` 会把项目的 Native 默认语言持久化到 `.comet/config.yaml`;后续 `new` 未显式传入 `--language` 时继承该值。再次运行 `init --language` 可以改变以后新建 change 的默认语言,不改写已有 change。已有配置拒绝冲突的 `--root`;改变根目录必须使用 `root move`,不能直接改配置。 + +## Change 管理 + +```text +comet native new [--language en|zh-CN] +comet native spec remove +comet native spec rebase --summary +comet native list [--cursor ] +comet native show +comet native status [--cursor ] +comet native status [--details [--acceptance-cursor ]] +comet native select +``` + +`new` 在配置缺失时创建默认配置和 `/docs/comet/`。完整目标规格写入 `specs//spec.md`;`next` 自动推断 create/replace 并冻结 canonical hash。删除 capability 使用 `spec remove`,不要手工编辑 `spec_changes`。 + +canonical 并发变化导致冲突时,先重读并改写完整目标规格,再用 `spec rebase` 刷新 operation/hash、回到 Build 并清除原验证结论。 + +`show` 返回状态、brief 和拟议完整规格。`status` 返回有预算的阶段、证据新鲜度、finding 摘要、checkpoint、repair 状态和 continuation。`status --details` 还会返回: + +- 最多 50 条详细 findings; +- `findingsTruncated` 标记; +- 恢复细节; +- 首个 `acceptancePage`。 + +findings 被截断时,先处理已返回项,再重新读取 details。`nextCursor` 非空时,用 `--acceptance-cursor` 逐页读取,直至为 null。acceptance cursor 只允许与具体 change 和 `--details` 同用,并绑定当前 acceptance hash。 + +`status` 与 `show` 始终只读。恢复已确认的目标 change 时显式运行 `select`,不要新增 `resume` 命令。`new` 与 `select` 都会写项目级共享 `.comet/current-change.json`,并把 `workflow` 固定为 `native`;它们不会修改 Classic change。 + +`list` 与不带 change 的 `status` 返回同一种只读分页投影,每页最多 24 个 change;`nextCursor` 非空时原样传给 `--cursor`。cursor 绑定当前完整名称集合,change 增删后旧 cursor 会明确失效,不会错位分页。最多接受 4096 个可见 change,整页序列化结果不超过 512 KiB。`show` 还会限制规格数量、单文件、累计读取和最终输出大小;超限时拒绝,不截断需求正文。 + +## 阶段内进度与内置检查 + +```text +comet native checkpoint \ + --summary \ + --next-action \ + [--artifact ]... \ + [--expect-revision ] + +comet native check +``` + +`checkpoint` 只保存同阶段摘要、下一动作和内容寻址的产物 manifest;它通过 revision/CAS 防止覆盖,不改变 phase。`check` 只允许在 Verify 且已有 implementation scope 时运行 Comet 内置的有界只读文本扫描。它不调用 Git、shell、项目脚本、外部 Skill 或任何外部进程,不接受任意命令、路径、环境或超时参数,也不修改项目文件或 change/Run/trajectory;结果、issue 计数和 scope 新鲜度会写入独立的内容寻址 receipt。检查发现问题或 stale 返回 1,但 receipt 仍会落盘。 + +```text +comet native evidence format [--entries ] +``` + +写 verification.md 的 `# Acceptance evidence` 机器块前,用这个命令把条目数组序列化成规范 Markdown 文本再粘贴,不要手工排版 JSON。默认从 stdin 读取条目 JSON,也可用 `--entries ` 指定文件;输出已包含 start/end markers、固定排序与缩进,原样粘贴即可通过 `next --result` 的规范序列化校验。 + +## 阶段推进 + +```text +comet native next --summary \ + [--confirmed] \ + [--artifact ]... \ + [--no-code-reason ] \ + [--allow-partial-scope --partial-reason --confirmed] \ + [--result pass|fail] \ + [--report ] \ + [--receipt ] \ + [--failure-category ]... \ + [--failed-check ]... \ + [--override-repair --override-summary ] + +comet native archive --dry-run +comet native archive --expect-preflight +``` + +- Shape:brief 和拟议规格通过后推进。Sequential 与 Batch 都必须取得最终共享理解确认并传 `--confirmed`。成功进入 Build 时,Runtime 会把 confirmed approval 绑定到当前 contract hash。 +- Build:重新检查 brief 和拟议规格;至少给出一个真实项目产物,或使用 `--no-code-reason`。旧 change 若仍为 `approval: implicit`,必须先确认当前共享理解;若 contract 在 approval 后变化,status/next 会要求用户重新确认当前 contract。两种情况都只有取得确认后才传 `--confirmed`。无法证明完整 scope 时,第一次调用返回 scope hash 与有界未归属明细而不推进;超出明细预算的变化由 `scope-detail-overflow` 的数量与内容 hash 表示。只有用户接受具体风险后,才可用完全匹配的 `--allow-partial-scope`、理由与 `--confirmed` 重试。 +- Verify:必须提供 `--result` 和完整 `--report`;可选 `--receipt` 必须是当前 change、revision、contract 与 implementation scope 上 fresh 的内置 receipt。fail 回到 Build,可用失败分类和检查 ID 形成无进展签名;pass 进入 Archive。 +- Repair:第三次相同失败会返回 manual stop。scope 真正变化时普通 Build `next` 会结束旧 repair episode 并继续;scope 不变时只能用 status 返回的 signature 和非空摘要 override 一次。单个 episode 的 semantic repair budget 与已耗尽 override 不可绕过;通用 Run iteration 只提供事件序号,不是长期 change 的永久停止条件。 +- Archive:只能由 `archive` 命令完成,不能用 `next` 代替。先 `--dry-run`,再把同一次预演返回的 `preflightHash` 原样传给 `--expect-preflight`;Runtime 在锁内重算后才提交。 + +## 诊断与恢复 + +```text +comet native doctor [] +comet native doctor [] --repair +comet native doctor [] --repair [--strategy continue|rollback] +``` + +只读 doctor 不改文件。`--repair` 只处理可证明安全的 selection、陈旧锁、evidence retention、普通阶段 transition、workspace 身份修复和确定性事务恢复;用户编写的 YAML、Markdown 与规格不会被自动重写。 + +`--strategy` 是可选的事务恢复参数,不是普通 repair 的必填项。普通 transition 只支持 `continue`,不支持 `rollback`。 + +doctor 也会只读报告 evidence retention 候选。显式 `--repair` 只清理 active change 中至少 30 天、每种 evidence kind 最新 32 份之外、且依赖闭包证明未引用的派生 evidence/receipt;归档证据、当前状态引用、依赖项、较新文件和每类最新 32 份始终保留。删除按 dependents-before-dependencies 排序,并先进入同目录 quarantine;中断后只读 doctor 报告 recovery required,显式 repair 在无覆盖且身份匹配时恢复。存在 pending journal、损坏、原文件与 quarantine 冲突或未知/特殊文件时 fail closed,不为腾空间冒险删除。 + +普通 `new`、`next`、`archive`、`root move` 等写命令不会自动接管陈旧锁。只有显式 `doctor --repair` 会在证明本机 owner 已不存在、锁身份未变化且没有相冲突恢复事务时接管;活动锁和无法证明陈旧的锁始终保留。 + +## 输出与退出码 + +所有命令支持 `--json`。JSON 模式只输出一个对象,包含 `command`、`exitCode`、`data`,失败时还包含结构化 `error`。 + +| 退出码 | 含义 | +| --- | --- | +| `0` | 成功 | +| `1` | 内置 `check` 完成但发现问题或结果 stale | +| `64` | 参数或用法错误 | +| `65` | 配置、状态或产物无效 | +| `73` | 锁、事务、并发 hash 或根目录冲突 | +| `75` | repair stagnation 或 hard stop 阻塞继续 | +| `70` | 未预期的内部失败 | diff --git a/.pi/skills/comet-native/reference/recovery.md b/.pi/skills/comet-native/reference/recovery.md new file mode 100644 index 0000000..5d7e552 --- /dev/null +++ b/.pi/skills/comet-native/reference/recovery.md @@ -0,0 +1,143 @@ +# Native 恢复参考 + +## 上下文恢复顺序 + +每次恢复都从磁盘事实开始: + +1. 读取项目 `.comet/config.yaml`,确认唯一 artifact root 和 `native.clarification_mode`;该字段缺失时使用 `sequential`。若有 `pending_root_move`,先运行 doctor。 +2. 运行只读的 `comet native status`;多个 active change 时读取项目级共享 `.comet/current-change.json`,确认 `workflow: native` 和目标 change,或让用户明确选择。 +3. 对目标 change 运行只读的 `show` 和 `status --details`,读取 `comet-state.yaml`、brief、拟议完整规格、verification、有界结构化 findings、`findingsTruncated` 标记和最新 checkpoint。若 findings 被截断,先处理已返回项,再重新读取 details;Verify/Archive 的验收 ID 则按 `acceptancePage.nextCursor` 独立分页取得,不依赖丢失的旧响应。 +4. 目标确认后运行 `comet native select ` 建立共享 selection;没有单独的 `resume` 命令,且只读命令不会隐式选择 change。 +5. 读取相关 canonical 规格、实现、规则、测试和当前工作区状态。 +6. 根据 phase 执行 Shape、Build、Verify 或 Archive,不依赖聊天记录猜阶段。 + +状态、Run state、trajectory 或 transaction journal 畸形时停止写入并运行只读 doctor。不要通过手工改 phase 来绕过问题。 + +## 澄清轮次恢复 + +Shape 或 Build 存在 `[blocking]` 时,从 brief 的 Open questions 恢复当前未决事项,不依赖聊天记录重建答案。Sequential 模式恢复一个最上游问题;Batch 模式按已保存编号恢复本轮全部可回答问题。切换配置不会消除已有阻塞项:先把用户答案对应回已保存问题,再按当前 `clarification_mode` 计算下一轮。 + +能从仓库、工具或运行环境查明的事实继续由 Agent 调查。宿主支持并行工作时可以并行查证,但恢复不能依赖任何可选的并行能力;调查中的事实只延后依赖它的问题。 + +Sequential 模式恢复当前最上游问题,Batch 模式恢复所有仍未回答的编号问题;两者都继续保持 `[blocking]`。全部问题解决后仍要恢复或建立最终共享理解确认;只有用户明确确认后,才能移除该阻塞项并进入 Build。该过程不增加新 phase、change 状态字段或独立决策树文件。 + +长任务在同一 phase 内中断前可写 checkpoint: + +```text +comet native checkpoint \ + --summary <已完成事实> \ + --next-action <下一动作> \ + [--artifact <项目相对路径>] \ + [--expect-revision ] +``` + +恢复时先检查 checkpoint 的 freshness。phase、revision 或 manifest 已变化时把它当历史提示,不把旧下一动作强行应用到新状态;Runtime 会在 details 中显式报告 stale 原因。 + +## 普通阶段推进 + +`next` 在 change 的 `runtime/transition.json` 先写入 prepared journal,再更新 Run state、`comet-state.yaml`、trajectory 和 checkpoint。全部完成后才删除 journal。 + +`status` 和 doctor 会报告未完成 transition。再次运行 `next` 或进入 Archive 时,runtime 会先确定性续做;也可以显式执行: + +```text +comet native doctor --repair --strategy continue +``` + +普通阶段推进没有 canonical 文件副作用,因此只支持 `continue`,不支持 `rollback`。journal 畸形时保留原文件并停止,不手工拼接状态。 + +## Baseline 缺失或不完整 + +`new` 要求 baseline 完整。Git 项目只评估 tracked 与未被 ignore 的 untracked 文件;ignored 缓存和嵌套仓库内容不会制造省略。 + +若 Runtime 返回 `baseline-snapshot-missing` 或 `baseline-snapshot-incomplete`,不要用当前文件重建 baseline,也不要把它当成 doctor 可自动修复的问题。这会丢失 change 创建以来的历史差异。 + +只能从可信备份恢复原 baseline,或保留用户编写的 brief、规格和实现事实后新建 change,重新建立完整 baseline。 + +## 证据失效与受控回退 + +进入 Verify 后,若 brief、拟议规格或项目快照变化,status 会先把失效的 implementation scope 指向受控回退;运行返回的仅含摘要的 `next` 命令回到 Build,再为变化后的 contract 取得重新确认并生成新 scope。进入 Archive 后,implementation scope、verification report 或 check receipt 等任一绑定事实变化也会触发同一回退。不要删除 finding、沿用旧 pass 或手工替换 hash ref。 + +Archive 必须使用两步预演,不能把一次旧的 ready 判断当作提交授权: + +```text +comet native archive --dry-run +comet native archive --expect-preflight <刚返回的-sha256> +``` + +第二步在锁内重算 contract、canonical base、scope、verification、当前 root 冲突与恢复状态;任何变化都会拒绝并要求重新预演。 + +## Verify fail 与修复停止 + +Verify fail 会诚实回到 Build。提交稳定、非敏感的 `--failure-category` 和 `--failed-check` 后,Runtime 先校验 token、数量与边界,再以 failure + contract + scope 形成签名:同签名第二次告警,第三次且 scope 无进展时 manual stop,单个 episode 累计到语义上限时 hard stop。 + +- implementation scope 真正变化:说明已有机械进展,普通 Build `next` 结束旧 episode 并自动开始新一轮;之前的 hard stop 不会锁死新实现。 +- scope 未变化但有一个明确新假设:只能使用 status 返回的 signature 和非空摘要做一次 `--override-repair`。 +- 同一签名已 override、或达到 hard stop:不能弱化验证或伪造 pass;保留现场并请用户决定范围、约束或是否停止。 + +一次 pass 会结束当前 repair episode;之后若旧 Archive 证据失效并重新出现相同 failure,它是新的 episode,但原 trajectory 仍保留审计事实。通用 Engine iteration budget 不参与这个产品语义。 + +## Canonical spec 冲突 + +若另一个 change 在当前 change 冻结 `base_hash` 后改变了同一 canonical spec,Archive 会停止。不要手改 hash: + +1. 重读最新 canonical spec、brief 和拟议完整规格; +2. 按用户意图改写完整目标规格,必要时先解决一个用户决定; +3. 运行 `comet native spec rebase --summary <摘要>`; +4. runtime 刷新 operation/hash,把 change 受控重开到 Build,并清除旧验证结论; +5. 重新实现、在需要时用 `--confirmed` 记录刚确认的决定、重新 Verify 和 Archive。 + +若 remove 的目标已经被并发 change 删除,rebase 会移除已满足的 remove 意图;其他 remove 会冻结最新 canonical hash 后重新验证。 + +## 当前工作区内的并行提示 + +status/Archive 会比较当前 Native root 中可见 change 的 capability、operation、base hash 和声明产物:确定冲突必须先解决;可能重叠也会在归档前阻塞。它不能看到未集成 worktree、远端分支或其他机器,因此不是分布式锁。 + +`workspace-root-changed` 和 `workspace-inspection-unavailable` 是显式 advisory,只用于解释当前 root 的事实来源,不单独阻止推进或归档。finding 会列出 `native-root-ref`、`project-root-path`、`native-root-path` 等具体漂移组件。Native 默认不读取 Git branch、HEAD 或 worktree changed paths。 + +不要把任意 `workspace-*` 都当成提示。未知 workspace 完整性 finding 仍按错误处理;Runtime 要求修复 workspace 身份时,先运行只读 doctor,再按报告执行显式 `doctor --repair`。 + +## Archive 事务 + +Archive 使用全局锁、staged specs、逐操作事件日志和备份。中断时 canonical 树可能处于事务中间状态,但 journal 会保留未完成事实。 + +Archive 的 stage、backup、apply 与 rollback copy 都通过受保护句柄读取:打开前后复核源文件、realpath、父目录身份、大小与预期 hash,目标在原子提交前再次检查。它不会因为事前路径包含检查通过,就信任后续路径仍指向同一文件。 + +write/remove 会先把 canonical 原对象身份和原内容绑定进事务,再原子改名到同目录隔离位并复验;write 使用事务私有 candidate 和无覆盖安装,rollback 也必须先隔离并验证 post 对象,再无覆盖恢复 original。即使并发替换后的内容 hash 恰好相同,只要它已经是另一个文件对象,Runtime 仍会拒绝覆盖并保留现场。 + +`events.jsonl` 读取有总字节、事件数量和单事件预算。崩溃若只留下最后一个 canonical JSON 事件的可证明前缀,Runtime 会在下一次 append 前按原始 bytes 的 hash/size 做 CAS,并原子重写到最后一个完整事件;中间坏行、完整但非法的尾行、非规范 JSON 尾部或并发改写一律 fail closed。完整但没有末尾换行的事件不会被误删,重复操作按 `type + operationId` 保持 exactly-once。 + +```text +comet native doctor +comet native doctor --repair --strategy continue +comet native doctor --repair --strategy rollback +``` + +- `continue`:从最后一个已完成操作继续,收敛到 committed archive。 +- `rollback`:按逆序恢复 canonical 文件和 active change。 +- Runtime 会先验证已移动的 archive tree、最终 state、受保护 Run、trajectory 事件和完成决定,随后才写 `archive-finalization-started`。标记写入前仍可安全 rollback;标记写入后已经跨越不可回滚边界,只能 continue,避免产生“已完成证据但又恢复为 active”的矛盾状态。 + +先阅读 doctor 的路径、transaction id 和冲突信息。若 hash 与 journal 两端都不一致,保留所有树并停止自动修复。 + +## Artifact root 迁移 + +`root move` 依次经过 `copying`、`ready`、`switched`。配置中的 `pending_root_move` 是恢复事实源;存在时普通 Native 写命令会失败关闭。 + +- `copying`:旧 root 是当前根,目标 staging 可能不完整。 +- `ready`:staging 已通过逐文件路径、大小和 SHA-256 校验,尚未切换配置。 +- `switched`:配置已指向新 root,旧 root 只有在再次验证两棵树等价、父链与目录身份后,才会先改名到事务 ID 绑定的 sibling quarantine,再复核删除。中断时 continue/rollback 会识别该 quarantine 并确定性收口,不会按旧路径直接递归删除。 + +使用 doctor 的显式 continue 或 rollback。若两棵树 hash 不一致,不删除任何一棵,并把报告中的两条路径交给用户处理。 + +## 锁与安全修复 + +doctor 区分活动锁、可证明陈旧的本机锁和无法判断的远端锁。只在 owner 进程确定不存在且没有未处理事务依赖时删除陈旧锁;不自动破坏活动或未知锁。 + +锁同时绑定 owner 元数据、锁文件身份和进程内 FIFO。普通 mutation 不会因为看到“看似过期”的时间戳就自动恢复;只有显式 doctor repair 才能执行带身份复验的 takeover,避免旧 owner 在新 owner 获锁后删除新锁而形成 split-brain。 + +doctor 可以安全清理指向不存在 change 的 selection。它不会自动重写损坏的配置、change YAML、brief、规格或 verification;这些内容必须根据用户意图人工修正后重新检查。 + +Evidence retention 遵守显式修复边界。默认 doctor 只报告候选;`--repair` 仅删除至少 30 天、每种 evidence kind 最新 32 份之外,并且依赖闭包证明未引用的 active-change 派生 evidence/receipt。 + +删除按 dependents-before-dependencies 排序,并先改名到同目录唯一 `.gc` quarantine。若在最终删除前中断,后续只读 doctor 报告 `evidence-retention-recovery-required`;显式 repair 仅在原路径不存在、quarantine 内容与身份有效时无覆盖恢复。 + +出现原文件与 quarantine 冲突、多份 quarantine、归档证据、pending 恢复、缺失依赖、损坏文档、未知目录项、symlink 或其他特殊文件时,推迟清理并失败关闭。 diff --git a/.pi/skills/comet-native/scripts/comet-native-hook-guard.mjs b/.pi/skills/comet-native/scripts/comet-native-hook-guard.mjs new file mode 100644 index 0000000..d7cc167 --- /dev/null +++ b/.pi/skills/comet-native/scripts/comet-native-hook-guard.mjs @@ -0,0 +1,4 @@ +#!/usr/bin/env node +import { main } from './comet-native-runtime.mjs'; + +process.exitCode = await main(['hook-guard', ...process.argv.slice(2)]); diff --git a/.pi/skills/comet-native/scripts/comet-native-runtime.mjs b/.pi/skills/comet-native/scripts/comet-native-runtime.mjs new file mode 100644 index 0000000..bd78ee8 --- /dev/null +++ b/.pi/skills/comet-native/scripts/comet-native-runtime.mjs @@ -0,0 +1,29563 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path48) { + const ctrl = callVisitor(key, node, visitor, path48); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path48, ctrl); + return visit_(key, ctrl, visitor, path48); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path48 = Object.freeze(path48.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path48); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path48 = Object.freeze(path48.concat(node)); + const ck = visit_("key", node.key, visitor, path48); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path48); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path48) { + const ctrl = await callVisitor(key, node, visitor, path48); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path48, ctrl); + return visitAsync_(key, ctrl, visitor, path48); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path48 = Object.freeze(path48.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path48); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path48 = Object.freeze(path48.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path48); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path48); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path48) { + if (typeof visitor === "function") + return visitor(key, node, path48); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path48); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path48); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path48); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path48); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path48); + return void 0; + } + function replaceNode(key, path48, node) { + const parent = path48[path48.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path48, value) { + let v = value; + for (let i = path48.length - 1; i >= 0; --i) { + const k = path48[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path48) => path48 == null || typeof path48 === "object" && !!path48[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path48, value) { + if (isEmptyPath(path48)) + this.add(value); + else { + const [key, ...rest] = path48; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path48) { + const [key, ...rest] = path48; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path48, keepScalar) { + const [key, ...rest] = path48; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path48) { + const [key, ...rest] = path48; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path48, value) { + const [key, ...rest] = path48; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify4(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify4.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify4.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify4 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify4.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify5 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify5(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify4.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify4.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp2 = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp2.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp2 = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp2.intTime, + timestamp2.floatTime, + timestamp2.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp2 = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp2.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp2.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp2.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp2.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify4 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify4.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify4.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify4.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path48, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path48, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path48) { + if (Collection.isEmptyPath(path48)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path48) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path48, keepScalar) { + if (Collection.isEmptyPath(path48)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path48, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path48) { + if (Collection.isEmptyPath(path48)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path48) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path48, value) { + if (Collection.isEmptyPath(path48)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path48), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path48, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify4 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path48) => { + let item = cst; + for (const [field2, index] of path48) { + const tok = item?.[field2]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path48) => { + const parent = visit.itemAtPath(cst, path48.slice(0, -1)); + const field2 = path48[path48.length - 1][0]; + const coll = parent?.[field2]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path48, item, visitor) { + let ctrl = visitor(item, path48); + if (typeof ctrl === "symbol") + return ctrl; + for (const field2 of ["key", "value"]) { + const token = item[field2]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path48.concat([[field2, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field2 === "key") + ctrl = ctrl(item, path48); + } + } + return typeof ctrl === "function" ? ctrl(item, path48) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs34 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs34, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs34); + } else { + Object.assign(it, { key: fs34, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs34 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs34, sep: [] }); + else if (it.sep) + this.stack.push(fs34); + else + Object.assign(it, { key: fs34, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument5(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument5(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify4(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument5; + exports.stringify = stringify4; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/comet-native/native-cli-entry.ts +import { pathToFileURL } from "url"; + +// domains/comet-native/native-cli.ts +import path47 from "path"; + +// platform/fs/race-safe-read.ts +import { constants as fsConstants, promises as fs } from "fs"; + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function hasComparableFileObject(left, right) { + return hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev) && hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// platform/fs/race-safe-read.ts +var RaceSafeReadError = class extends Error { + reason; + constructor(reason, message, options) { + super(message, options); + this.name = "RaceSafeReadError"; + this.reason = reason; + } +}; +function birthtimeOf(stat) { + return "birthtimeNs" in stat && typeof stat.birthtimeNs === "bigint" ? stat.birthtimeNs : stat.birthtimeMs; +} +function ctimeOf(stat) { + return "ctimeNs" in stat && typeof stat.ctimeNs === "bigint" ? stat.ctimeNs : stat.ctimeMs; +} +function identityOf(stat) { + return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) }; +} +function sameStatIdentity(left, right) { + const leftObject = identityOf(left); + const rightObject = identityOf(right); + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && birthtimeOf(left) === birthtimeOf(right) && ctimeOf(left) === ctimeOf(right) && left.size === right.size; +} +async function readFileRaceSafe(file, maxBytes, options = {}) { + if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) { + throw new Error("race-safe read byte limit must be a positive integer"); + } + const label = options.label ?? "file"; + const bigint = options.bigint === true; + const before = await fs.lstat(file, { bigint }); + if (!before.isFile() || before.isSymbolicLink()) { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`); + } + if (BigInt(before.size) > BigInt(maxBytes)) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + const beforeRealPath = await fs.realpath(file); + await options.verify?.("pre-open", { realPath: beforeRealPath, identity: identityOf(before) }); + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + let handle; + try { + handle = await fs.open(file, flags); + } catch (error) { + if (error.code === "ELOOP") { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`, { + cause: error + }); + } + throw error; + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat({ bigint }), + fs.lstat(file, { bigint }), + fs.realpath(file) + ]); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameStatIdentity(before, opened) || !sameStatIdentity(before, pathAfterOpen)) { + throw new RaceSafeReadError("changed", `${label} changed while opening`); + } + await options.verify?.("post-open", { + realPath: realPathAfterOpen, + identity: identityOf(opened) + }); + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + for (; ; ) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat({ bigint }), + fs.lstat(file, { bigint }), + fs.realpath(file) + ]); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameStatIdentity(before, afterHandle) || !sameStatIdentity(before, afterPath)) { + throw new RaceSafeReadError("changed", `${label} changed while reading`); + } + await options.verify?.("post-read", { + realPath: afterRealPath, + identity: identityOf(afterHandle) + }); + return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-archive.ts +import { randomUUID as randomUUID6 } from "crypto"; +import { promises as fs23 } from "fs"; +import path34 from "path"; +import { isDeepStrictEqual as isDeepStrictEqual2 } from "util"; + +// domains/engine/loop.ts +import { createHash } from "crypto"; + +// domains/engine/guardrails.ts +function checkAction(action, state, guardrails, confirmations) { + if (state.iteration >= guardrails.maxIterations) { + return { allowed: false, reason: `Iteration budget exhausted: ${guardrails.maxIterations}` }; + } + if (action.type === "invoke_skill" && !guardrails.allowedSkills.includes(action.ref ?? "")) { + return { allowed: false, reason: `Skill is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.type === "call_tool" && !guardrails.allowedTools.includes(action.ref ?? "")) { + return { allowed: false, reason: `Tool is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.type === "handoff" && !guardrails.allowedAgents.includes(action.ref ?? "")) { + return { allowed: false, reason: `Agent is not allowed: ${action.ref ?? "(missing)"}` }; + } + if (action.ref && guardrails.confirmationRequiredFor.includes(action.ref) && !confirmations.has(action.ref)) { + return { allowed: false, reason: `User confirmation required for: ${action.ref}` }; + } + const retries2 = state.retries[action.id] ?? 0; + if (retries2 > guardrails.maxRetriesPerAction) { + return { allowed: false, reason: `Retry budget exhausted for action: ${action.id}` }; + } + return { allowed: true }; +} + +// domains/engine/resolver.ts +function deepFreeze(value) { + if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value; + for (const key of Reflect.ownKeys(value)) { + deepFreeze(value[key]); + } + return Object.freeze(value); +} +function readonlyCopy(value) { + return deepFreeze(structuredClone(value)); +} +function resolveDeterministicStep(resolver, pkg, state, context) { + return resolver.resolveStep({ + pkg: readonlyCopy(pkg), + state: readonlyCopy(state), + context: readonlyCopy(context) + }); +} +function resolveDeterministicNext(resolver, pkg, state, step, outcome, context) { + return resolver.resolveNext({ + pkg: readonlyCopy(pkg), + state: readonlyCopy(state), + step: readonlyCopy(step), + outcome: readonlyCopy(outcome), + context: readonlyCopy(context) + }); +} + +// domains/engine/loop.ts +function actionId(runId, iteration, stepId) { + return createHash("sha256").update(`${runId}:${iteration}:${stepId ?? "adaptive"}`).digest("hex").slice(0, 16); +} +function stepFor(pkg, id) { + return pkg.definition.orchestration.steps?.find((step) => step.id === id); +} +function decideWithResolver(pkg, state, confirmations, resolver, context) { + if (state.status !== "running") return { state, action: null, reason: `Run is ${state.status}` }; + if (state.pending) + return { state, action: null, reason: `Action already pending: ${state.pending}` }; + if (state.orchestration === "adaptive") { + return { state, action: null, reason: "Adaptive orchestration requires an Agent candidate" }; + } + const resolvedStep = resolveDeterministicStep(resolver, pkg, state, context); + if (!resolvedStep && state.currentStep === null) { + return { state: { ...state, status: "completed" }, action: null }; + } + if (!resolvedStep) { + return { + state: { ...state, status: "failed" }, + action: null, + reason: `Unknown current step: ${state.currentStep}` + }; + } + const step = stepFor(pkg, resolvedStep.id); + if (!step) { + return { + state: { ...state, status: "failed" }, + action: null, + reason: `Resolver returned unknown current step: ${resolvedStep.id}` + }; + } + const resolvedState = state.currentStep === step.id ? state : { ...state, currentStep: step.id }; + const action = { + ...step.action, + id: actionId(resolvedState.runId, resolvedState.iteration, step.id), + stepId: step.id + }; + return acceptAction(pkg, resolvedState, action, confirmations); +} +function acceptAction(pkg, state, action, confirmations) { + const guard = checkAction(action, state, pkg.guardrails, confirmations); + if (!guard.allowed) return { state, action: null, reason: guard.reason }; + return { state: { ...state, pending: action.id, status: "waiting" }, action }; +} +function recordOutcomeWithResolver(pkg, state, outcome, resolver, context) { + if (!state.pending || state.pending !== outcome.actionId) { + throw new Error(`Outcome does not match pending action: ${outcome.actionId}`); + } + const resolvedStep = state.orchestration === "deterministic" ? resolveDeterministicStep(resolver, pkg, state, context) : void 0; + const step = resolvedStep ? stepFor(pkg, resolvedStep.id) : void 0; + if (state.orchestration === "deterministic" && !resolvedStep) { + throw new Error(`Unknown current step: ${state.currentStep ?? "(missing)"}`); + } + if (state.orchestration === "deterministic" && !step) { + throw new Error(`Resolver returned unknown current step: ${resolvedStep.id}`); + } + if (outcome.status === "failed") { + const retries2 = { + ...state.retries, + [outcome.actionId]: (state.retries[outcome.actionId] ?? 0) + 1 + }; + return { ...state, pending: null, status: "running", retries: retries2 }; + } + const next = state.orchestration === "deterministic" ? resolveDeterministicNext(resolver, pkg, state, step, outcome, context) : state.currentStep; + if (next !== null && state.orchestration === "deterministic" && !stepFor(pkg, next)) { + throw new Error(`Resolver returned unknown next step: ${next}`); + } + return { + ...state, + currentStep: next, + iteration: state.iteration + 1, + pending: null, + status: next === null && state.orchestration === "deterministic" ? "completed" : "running" + }; +} + +// domains/comet-native/native-artifacts.ts +import { promises as fs14 } from "fs"; +import path17 from "path"; + +// domains/comet-native/native-change.ts +var import_yaml3 = __toESM(require_dist(), 1); +import { promises as fs13 } from "fs"; +import path16 from "path"; + +// domains/comet-native/native-bounded-file.ts +import { createHash as createHash2 } from "node:crypto"; +import { promises as fs2 } from "node:fs"; +import path2 from "node:path"; +import { TextDecoder } from "node:util"; + +// domains/comet-native/native-sensitive-paths.ts +import path from "path"; +var NATIVE_EXCLUDED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([ + ".cache", + ".git", + ".gradle", + ".gnupg", + ".mypy_cache", + ".next", + ".npm", + ".pnpm-store", + ".pytest_cache", + ".ssh", + ".turbo", + ".venv", + ".yarn", + "__pycache__", + "node_modules", + "venv" +]); +var NATIVE_SENSITIVE_FILE_NAMES = /* @__PURE__ */ new Set([ + ".git-credentials", + ".netrc", + ".npmrc", + ".pypirc", + "auth.json", + "credentials.json" +]); +function isNativeEnvFileName(name) { + return name.toLowerCase().startsWith(".env"); +} +function nativeSensitiveRelativePathReason(relativeRef) { + const segments = relativeRef.replaceAll("\\", "/").split("/").filter(Boolean); + const lower = segments.map((segment) => segment.toLowerCase()); + if (lower.some((segment) => isNativeEnvFileName(segment))) return "environment-file"; + if (lower.some((segment) => NATIVE_SENSITIVE_FILE_NAMES.has(segment))) { + return "credential-config"; + } + if (lower.includes(".git")) return "git-metadata"; + if (lower.some((segment) => NATIVE_EXCLUDED_DIRECTORY_NAMES.has(segment))) { + return "dependency-or-cache"; + } + if (lower.join("/") === ".comet/config.yaml") { + return "comet-config"; + } + if (lower.join("/") === ".comet/current-change.json") { + return "comet-selection"; + } + return null; +} +function nativeSensitiveArtifactReason(paths, relativeRef) { + const generic = nativeSensitiveRelativePathReason(relativeRef); + if (generic) return generic; + const target = path.resolve(paths.projectRoot, ...relativeRef.split("/")); + const relativeNativeRoot = path.relative(paths.projectRoot, paths.nativeRoot).replaceAll("\\", "/"); + const normalized = relativeRef.replaceAll("\\", "/"); + if (normalized === relativeNativeRoot || normalized.startsWith(`${relativeNativeRoot}/`) || target === path.resolve(paths.configFile)) { + return "native-runtime"; + } + return null; +} + +// domains/comet-native/native-bounded-file.ts +var DEFAULT_NATIVE_ARTIFACT_MAX_BYTES = 1024 * 1024; +function isInside(parent, target) { + const relative = path2.relative(parent, target); + return relative === "" || !path2.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path2.sep}`); +} +function portableArtifactRef(value) { + const normalized = path2.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path2.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`Native artifact ref must be normalized and relative: ${value}`); + } + const lower = value.toLowerCase(); + const sensitiveReason = nativeSensitiveRelativePathReason(value); + if (sensitiveReason || lower === "runtime" || lower.startsWith("runtime/")) { + throw new Error( + `Native artifact ref is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${value}` + ); + } + return value; +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native artifact byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function directoryIdentity(directory) { + const stat = await fs2.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native artifact parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs2.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory) { + const lexicalRoot = path2.resolve(root); + const lexicalDirectory = path2.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error("Native artifact path is outside its root"); + } + const chain = [await directoryIdentity(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path2.relative(lexicalRoot, lexicalDirectory).split(path2.sep).filter(Boolean)) { + cursor = path2.join(cursor, segment); + const identity = await directoryIdentity(cursor); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`Native artifact parent resolves outside its root: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain(chain) { + for (const identity of chain) { + const stat = await fs2.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs2.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native artifact parent changed while reading: ${identity.path}`); + } + } +} +async function readNativeBoundedTextFile(options) { + const ref = portableArtifactRef(options.ref); + const maxBytes = positiveLimit(options.maxBytes ?? DEFAULT_NATIVE_ARTIFACT_MAX_BYTES); + const file = path2.resolve(options.root, ...ref.split("/")); + const chain = await captureDirectoryChain(options.root, path2.dirname(file)); + await options.hooks?.afterParentChainCaptured?.(); + const before = await fs2.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native artifact must be a regular file: ${ref}`); + } + if (before.size > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + const realPath = await fs2.realpath(file); + if (!isInside(chain[0].realPath, realPath)) { + throw new Error(`Native artifact resolves outside its root: ${ref}`); + } + const handle = await fs2.open(file, "r"); + try { + const [opened, afterOpenPath, afterOpenRealPath] = await Promise.all([ + handle.stat(), + fs2.lstat(file), + fs2.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!opened.isFile() || !afterOpenPath.isFile() || afterOpenPath.isSymbolicLink() || afterOpenRealPath !== realPath || !sameFileIdentity(before, opened) || !sameFileIdentity(opened, afterOpenPath)) { + throw new Error(`Native artifact changed while opening: ${ref}`); + } + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (result2.bytesRead === 0) break; + total += result2.bytesRead; + if (total > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + chunks.push(Buffer.from(buffer.subarray(0, result2.bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs2.lstat(file), + fs2.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity(opened, afterHandle) || !sameFileIdentity(opened, afterPath)) { + throw new Error(`Native artifact changed while reading: ${ref}`); + } + const bytes = Buffer.concat(chunks, total); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`Native artifact is not valid UTF-8: ${ref}`, { cause: error }); + } + return { + ref, + size: total, + hash: createHash2("sha256").update(bytes).digest("hex"), + text + }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-atomic-file.ts +import { randomUUID } from "crypto"; +import { promises as fs3 } from "fs"; +import path3 from "path"; +function isInside2(parent, target) { + const relative = path3.relative(parent, target); + return relative === "" || !path3.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path3.sep}`); +} +function sameDirectoryIdentity2(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity2(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryIdentity(directory) { + const stat = await fs3.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native atomic write parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs3.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function verifyDirectoryChain2(chain) { + for (const identity of chain) { + const stat = await fs3.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity2(identity, stat) || await fs3.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native atomic write parent changed before commit: ${identity.path}`); + } + } +} +async function prepareContainedDirectoryChain(root, directory) { + const lexicalRoot = path3.resolve(root); + const lexicalDirectory = path3.resolve(directory); + if (!isInside2(lexicalRoot, lexicalDirectory)) { + throw new Error(`Native atomic write parent is outside its managed root: ${directory}`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot)]; + const segments = path3.relative(lexicalRoot, lexicalDirectory).split(path3.sep).filter(Boolean); + let cursor = lexicalRoot; + for (const segment of segments) { + await verifyDirectoryChain2(chain); + cursor = path3.join(cursor, segment); + try { + await fs3.mkdir(cursor); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const identity = await captureDirectoryIdentity(cursor); + if (!isInside2(chain[0].realPath, identity.realPath)) { + throw new Error(`Native atomic write parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain2(chain); + return chain; +} +async function syncDirectory(directory) { + let handle; + try { + handle = await fs3.open(directory, "r"); + await handle.sync(); + } catch (error) { + const code = error.code; + if (!["EACCES", "EBADF", "EINVAL", "EISDIR", "ENOTSUP", "EPERM"].includes(code ?? "")) { + throw error; + } + } finally { + await handle?.close(); + } +} +async function atomicWrite(file, content, options = {}) { + const directory = path3.dirname(file); + const directoryChain = options.containedRoot ? await prepareContainedDirectoryChain(options.containedRoot, directory) : null; + if (!directoryChain) await fs3.mkdir(directory, { recursive: true }); + const temporary = path3.join(directory, `.${path3.basename(file)}.${randomUUID()}.tmp`); + let handle; + let temporaryIdentity; + try { + await options.beforeTemporaryOpen?.(); + handle = await fs3.open(temporary, "wx"); + temporaryIdentity = await handle.stat(); + if (directoryChain) { + const [temporaryPathStat, temporaryRealPath] = await Promise.all([ + fs3.lstat(temporary), + fs3.realpath(temporary) + ]); + await verifyDirectoryChain2(directoryChain); + if (!temporaryPathStat.isFile() || temporaryPathStat.isSymbolicLink() || !sameFileIdentity2(temporaryIdentity, temporaryPathStat) || !isInside2(directoryChain[0].realPath, temporaryRealPath)) { + throw new Error("Native atomic write temporary file opened outside its managed parent"); + } + } + if (typeof content === "string") await handle.writeFile(content, "utf8"); + else await handle.writeFile(content); + await handle.sync(); + if (!sameFileIdentity2(temporaryIdentity, await handle.stat())) { + throw new Error("Native atomic write temporary file changed while writing"); + } + await handle.close(); + handle = void 0; + await options.beforeCommit?.(); + if (directoryChain) { + await verifyDirectoryChain2(directoryChain); + const temporaryStat = await fs3.lstat(temporary); + if (!temporaryStat.isFile() || temporaryStat.isSymbolicLink() || !temporaryIdentity || !sameFileIdentity2(temporaryStat, temporaryIdentity)) { + throw new Error("Native atomic write temporary file changed before commit"); + } + } + if (options.exclusive) { + await fs3.link(temporary, file); + await fs3.unlink(temporary); + } else { + await fs3.rename(temporary, file); + } + await syncDirectory(directory); + } catch (error) { + await handle?.close(); + if (!directoryChain) { + await fs3.rm(temporary, { force: true }); + } else { + try { + await verifyDirectoryChain2(directoryChain); + await fs3.rm(temporary, { force: true }); + } catch { + } + } + throw error; + } +} +async function atomicWriteText(file, content, options = {}) { + await atomicWrite(file, content, options); +} +async function atomicWriteBytes(file, content, options = {}) { + await atomicWrite(file, content, options); +} +async function atomicWriteJson(file, value, options = {}) { + await atomicWriteText(file, JSON.stringify(value, null, 2) + "\n", options); +} + +// domains/comet-native/native-config.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { promises as fs6 } from "fs"; +import path6 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml = __toESM(require_dist(), 1); +var COMMENTS = { + en: { + schema: "# Configuration schema used by Comet. Do not edit this value.", + default_workflow: "# Default workflow entered by /comet. Must also appear in workflows.", + workflows: "# Workflows enabled in this project: native, classic, or both.", + ambient_resume: "# Enables automatic recovery through the read-only Ambient Resume probe for both Native and Classic. Set false to disable it.\n# ambient_resume: true | false", + native: "# Native workflow settings. They do not change Classic state or behavior.", + "native.artifact_root": "# Root directory where Native stores Comet specs, changes, and runtime data.", + "native.language": "# Artifact language used by Native workflow documents.\n# language: en | zh-CN", + "native.clarification_mode": "# Controls whether Native asks one clarification at a time or every currently answerable question in a round.\n# clarification_mode: sequential | batch", + "native.snapshot": "# Controls the auditable project scope and bounded work used by Native content snapshots.", + "native.snapshot.include": "# Selects the project-relative paths included in Native snapshots. Patterns use / and support *, **, and ?.", + "native.snapshot.exclude": "# Removes paths from the included scope. Exclusions are bound into each new change baseline.", + "native.snapshot.max_files": "# Bounds the number of files captured by one snapshot. Increase it for large monorepos.", + "native.snapshot.max_total_bytes": "# Bounds the total file content hashed by one snapshot. Content is streamed and does not depend on Git hashes.", + "native.snapshot.max_duration_ms": "# Bounds snapshot capture time in milliseconds. Increase it together with the byte budget on slower or larger repositories.", + classic: "# Classic workflow settings. They do not change Native state or behavior.", + "classic.language": "# Artifact language used by Classic workflow documents.\n# language: en | zh-CN", + "classic.context_compression": "# Controls beta context compression for new Classic changes.\n# context_compression: off | beta", + "classic.review_mode": "# Sets the default review depth for new Classic changes.\n# review_mode: off | standard | thorough", + "classic.auto_transition": "# Automatically enters the next Classic phase after a phase passes.\n# auto_transition: true | false" + }, + "zh-CN": { + schema: "# Comet 使用的配置格式版本,请勿修改此值。", + default_workflow: "# `/comet` 默认进入的工作流;该值也必须出现在 workflows 中。", + workflows: "# 此项目启用的工作流,可填写 native、classic 或同时启用两者。", + ambient_resume: "# 是否启用只读的环境感知恢复探针,同时作用于 Native 和 Classic;设为 false 可关闭自动工作流恢复。\n# ambient_resume: true | false", + native: "# Native 工作流配置,不会改变 Classic 的状态或行为。", + "native.artifact_root": "# Native 产物的存放根目录,包括规格、change 和运行时数据。", + "native.language": "# Native 工作流文档使用的产物语言。\n# 可选值:en | zh-CN", + "native.clarification_mode": "# Native 每轮询问一个问题,或一次提出当前所有可回答的问题。\n# 可选值:sequential | batch", + "native.snapshot": "# Native 内容快照使用的可审计项目范围与有界工作预算。", + "native.snapshot.include": "# Native 快照纳入的项目相对路径;模式使用 /,支持 *、** 和 ?。", + "native.snapshot.exclude": "# 从纳入范围中排除路径;新 change 会把排除策略绑定到 baseline。", + "native.snapshot.max_files": "# 单次快照最多捕获的文件数;大型 monorepo 可按需提高。", + "native.snapshot.max_total_bytes": "# 单次快照最多哈希的文件内容总字节数;内容采用流式读取,不依赖 Git hash。", + "native.snapshot.max_duration_ms": "# 单次快照的最长执行时间(毫秒);较慢或更大的仓库应与字节预算一并提高。", + classic: "# Classic 工作流配置,不会改变 Native 的状态或行为。", + "classic.language": "# Classic 工作流文档使用的产物语言。\n# 可选值:en | zh-CN", + "classic.context_compression": "# 新建 Classic change 是否启用 beta 上下文压缩。\n# 可选值:off | beta", + "classic.review_mode": "# 新建 Classic change 默认使用的审查深度。\n# 可选值:off | standard | thorough", + "classic.auto_transition": "# Classic 阶段通过后是否自动进入下一阶段。\n# 可选值:true | false" + } +}; +function projectConfigComment(key, language) { + return COMMENTS[language][key]; +} +function commentKey(line, block, nativeNested) { + const match = /^(\s*)([a-z_]+):/u.exec(line); + if (!match) return null; + const indent = match[1].length; + const key = match[2]; + if (indent === 0 && key in COMMENTS.en) return key; + if (indent === 2 && block) { + const blockKey = `${block}.${key}`; + if (blockKey in COMMENTS.en) return blockKey; + } + if (indent === 4 && block === "native" && nativeNested === "snapshot") { + const nestedKey = `native.snapshot.${key}`; + if (nestedKey in COMMENTS.en) return nestedKey; + } + return null; +} +function renderStructuredProjectConfig(value, language) { + const output = []; + let block = null; + let nativeNested = null; + for (const line of (0, import_yaml.stringify)(value).trimEnd().split("\n")) { + const key = commentKey(line, block, nativeNested); + if (key) { + const indent = line.match(/^\s*/u)?.[0] ?? ""; + for (const comment of projectConfigComment(key, language).split("\n")) { + output.push(`${indent}${comment}`); + } + } + output.push(line); + if (/^[a-z_]+:/u.test(line)) { + if (line.startsWith("native:")) block = "native"; + else if (line.startsWith("classic:")) block = "classic"; + else block = null; + nativeNested = null; + } else if (/^ {2}[a-z_]+:/u.test(line) && block === "native") { + nativeNested = line.startsWith(" snapshot:") ? "snapshot" : null; + } + } + output.push(""); + return output.join("\n"); +} + +// domains/comet-native/native-protected-file.ts +import { createHash as createHash3 } from "node:crypto"; +import { constants as fsConstants2, promises as fs4 } from "node:fs"; +import path4 from "node:path"; +import { TextDecoder as TextDecoder2 } from "node:util"; +function isInside3(parent, target) { + const relative = path4.relative(parent, target); + return relative === "" || !path4.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path4.sep}`); +} +function positiveLimit2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity3(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity3(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity2(directory, label) { + const stat = await fs4.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs4.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain2(root, directory, label) { + const lexicalRoot = path4.resolve(root); + const lexicalDirectory = path4.resolve(directory); + if (!isInside3(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity2(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path4.relative(lexicalRoot, lexicalDirectory).split(path4.sep).filter(Boolean)) { + await verifyDirectoryChain3(chain, label); + cursor = path4.join(cursor, segment); + const identity = await captureDirectoryIdentity2(cursor, label); + if (!isInside3(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain3(chain, label); + return chain; +} +async function verifyDirectoryChain3(chain, label) { + for (const identity of chain) { + const stat = await fs4.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity3(identity, stat) || await fs4.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit2(options.maxBytes); + const file = path4.resolve(options.file); + const chain = await captureDirectoryChain2(options.root, path4.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity2(path4.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain3(chain, options.label); + const before = await fs4.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs4.realpath(file); + if (!isInside3(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside3(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants2.O_RDONLY : fsConstants2.O_RDONLY | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK; + const handle = await fs4.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs4.lstat(file), + fs4.realpath(file) + ]); + await verifyDirectoryChain3(chain, options.label); + await verifyDirectoryChain3(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity3(beforeIdentity, opened) || !sameFileIdentity3(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs4.lstat(file), + fs4.realpath(file) + ]); + await verifyDirectoryChain3(chain, options.label); + await verifyDirectoryChain3(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity3(beforeIdentity, afterHandle) || !sameFileIdentity3(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash3("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot2 = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder2("utf-8", { fatal: true }).decode(snapshot2.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot2, text }; +} +async function readNativeProtectedDirectory(options) { + const chain = await captureDirectoryChain2(options.root, options.directory, options.label); + let entries; + if (options.maxEntries === void 0) { + entries = await fs4.readdir(options.directory, { withFileTypes: true }); + } else { + const maxEntries = positiveLimit2(options.maxEntries); + entries = []; + const directory = await fs4.opendir(options.directory); + try { + for await (const entry2 of directory) { + entries.push(entry2); + if (entries.length > maxEntries) { + throw new Error(`${options.label} exceeds ${maxEntries} entries`); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + } + await verifyDirectoryChain3(chain, options.label); + return { + entries, + verify: () => verifyDirectoryChain3(chain, options.label) + }; +} +async function captureNativeProtectedDirectoryGuard(options) { + const chain = await captureDirectoryChain2(options.root, options.directory, options.label); + return { verify: () => verifyDirectoryChain3(chain, options.label) }; +} +async function quarantineNativeProtectedDirectoryInternal(options) { + const directory = path4.resolve(options.directory); + const quarantine = path4.resolve(options.quarantine); + if (path4.dirname(quarantine) !== path4.dirname(directory) || !isInside3(path4.resolve(options.root), quarantine) || quarantine === directory) { + throw new Error(`${options.label} quarantine must be a distinct sibling inside its root`); + } + const parentChain = await captureDirectoryChain2( + options.root, + path4.dirname(directory), + options.label + ); + const identity = await captureDirectoryIdentity2(directory, options.label); + if (!isInside3(parentChain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + await options.beforeQuarantine?.(); + await verifyDirectoryChain3(parentChain, options.label); + const current = await fs4.lstat(directory); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(identity, current) || await fs4.realpath(directory) !== identity.realPath) { + throw new Error(`${options.label} changed before quarantine`); + } + try { + await fs4.lstat(quarantine); + throw new Error(`${options.label} quarantine path is occupied`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await fs4.rename(directory, quarantine); + await verifyDirectoryChain3(parentChain, options.label); + const quarantined = await fs4.lstat(quarantine); + if (!quarantined.isDirectory() || quarantined.isSymbolicLink() || !sameDirectoryIdentity3(identity, quarantined)) { + throw new Error(`${options.label} changed while quarantining`); + } + await options.afterQuarantine?.(quarantine); + try { + await fs4.lstat(directory); + throw new Error(`${options.label} path was recreated while quarantining`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + return { quarantine, parentChain, identity }; +} +async function quarantineNativeProtectedDirectory(options) { + await quarantineNativeProtectedDirectoryInternal(options); +} +async function removeNativeProtectedFile(options) { + const file = path4.resolve(options.file); + const parentChain = await captureDirectoryChain2(options.root, path4.dirname(file), options.label); + const before = await fs4.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + const identity = asFileIdentity(before); + const realPath = await fs4.realpath(file); + if (!isInside3(parentChain[0].realPath, realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + const snapshot2 = await readNativeProtectedFile({ + root: options.root, + file, + maxBytes: options.maxBytes, + label: options.label + }); + if (snapshot2.hash !== options.expectedHash || snapshot2.size !== options.expectedSize) { + throw new Error(`${options.label} changed before removal`); + } + const [afterRead, afterReadRealPath] = await Promise.all([fs4.lstat(file), fs4.realpath(file)]); + if (!afterRead.isFile() || afterRead.isSymbolicLink() || !sameFileIdentity3(identity, afterRead) || afterReadRealPath !== realPath) { + throw new Error(`${options.label} changed while verifying removal`); + } + await options.beforeRemove?.(); + await verifyDirectoryChain3(parentChain, options.label); + const [current, currentRealPath] = await Promise.all([fs4.lstat(file), fs4.realpath(file)]); + if (!current.isFile() || current.isSymbolicLink() || !sameFileIdentity3(identity, current) || currentRealPath !== realPath) { + throw new Error(`${options.label} changed before removal`); + } + await fs4.rm(file); + await verifyDirectoryChain3(parentChain, options.label); +} +async function removeNativeProtectedEmptyDirectory(options) { + const directory = path4.resolve(options.directory); + const parentChain = await captureDirectoryChain2( + options.root, + path4.dirname(directory), + options.label + ); + const identity = await captureDirectoryIdentity2(directory, options.label); + if (!isInside3(parentChain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + await options.beforeRemove?.(); + await verifyDirectoryChain3(parentChain, options.label); + const current = await fs4.lstat(directory); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(identity, current) || await fs4.realpath(directory) !== identity.realPath) { + throw new Error(`${options.label} changed before removal`); + } + await fs4.rmdir(directory); + await verifyDirectoryChain3(parentChain, options.label); +} +async function ensureNativeProtectedDirectory(options) { + const lexicalRoot = path4.resolve(options.root); + const lexicalDirectory = path4.resolve(options.directory); + if (!isInside3(lexicalRoot, lexicalDirectory)) { + throw new Error(`${options.label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity2(lexicalRoot, options.label)]; + let cursor = lexicalRoot; + for (const segment of path4.relative(lexicalRoot, lexicalDirectory).split(path4.sep).filter(Boolean)) { + await verifyDirectoryChain3(chain, options.label); + cursor = path4.join(cursor, segment); + try { + await fs4.mkdir(cursor); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const identity = await captureDirectoryIdentity2(cursor, options.label); + if (!isInside3(chain[0].realPath, identity.realPath)) { + throw new Error(`${options.label} resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain3(chain, options.label); +} +async function moveNativeProtectedDirectory(options) { + const root = path4.resolve(options.root); + const source = path4.resolve(options.source); + const target = path4.resolve(options.target); + if (source === target || !isInside3(root, source) || !isInside3(root, target) || isInside3(source, target) || isInside3(target, source)) { + throw new Error(`${options.label} source and target must be distinct paths inside one root`); + } + await ensureNativeProtectedDirectory({ + root, + directory: path4.dirname(target), + label: `${options.label} target parent` + }); + const sourceParentChain = await captureDirectoryChain2(root, path4.dirname(source), options.label); + const targetParentChain = await captureDirectoryChain2(root, path4.dirname(target), options.label); + const sourceIdentity = await captureDirectoryIdentity2(source, options.label); + if (!isInside3(sourceParentChain[0].realPath, sourceIdentity.realPath)) { + throw new Error(`${options.label} source resolves outside its managed root`); + } + try { + await fs4.lstat(target); + throw new Error(`${options.label} target already exists`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await options.beforeMove?.(); + await Promise.all([ + verifyDirectoryChain3(sourceParentChain, options.label), + verifyDirectoryChain3(targetParentChain, options.label) + ]); + const current = await fs4.lstat(source); + if (!current.isDirectory() || current.isSymbolicLink() || !sameDirectoryIdentity3(sourceIdentity, current) || await fs4.realpath(source) !== sourceIdentity.realPath) { + throw new Error(`${options.label} source changed before move`); + } + await fs4.rename(source, target); + await Promise.all([ + verifyDirectoryChain3(sourceParentChain, options.label), + verifyDirectoryChain3(targetParentChain, options.label) + ]); + const moved = await fs4.lstat(target); + if (!moved.isDirectory() || moved.isSymbolicLink() || !sameDirectoryIdentity3(sourceIdentity, moved) || !isInside3(sourceParentChain[0].realPath, await fs4.realpath(target))) { + throw new Error(`${options.label} source identity changed while moving`); + } + try { + await fs4.lstat(source); + throw new Error(`${options.label} source was recreated while moving`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } +} +async function copyNativeProtectedFile(options) { + const source = await readNativeProtectedFile({ + root: options.sourceRoot, + file: options.source, + maxBytes: options.maxBytes, + label: options.label, + hooks: options.hooks, + forbiddenRoots: options.forbiddenRoots + }); + if (options.expectedHash !== void 0 && source.hash !== options.expectedHash) { + throw new Error(`${options.label} content changed before copy`); + } + await atomicWriteBytes(options.target, source.bytes, { + containedRoot: options.targetRoot, + exclusive: options.exclusive, + beforeCommit: async () => { + await options.hooks?.beforeTargetCommit?.(); + if (options.expectedTargetHash === void 0) return; + if (options.expectedTargetHash === null) { + try { + await fs4.lstat(options.target); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + throw new Error(`${options.label} target changed before commit`); + } + const target = await readNativeProtectedFile({ + root: options.targetRoot, + file: options.target, + maxBytes: options.maxBytes, + label: `${options.label} existing target` + }); + if (target.hash !== options.expectedTargetHash) { + throw new Error(`${options.label} target changed before commit`); + } + } + }); + const persisted = await readNativeProtectedFile({ + root: options.targetRoot, + file: options.target, + maxBytes: options.maxBytes, + label: `${options.label} target` + }); + if (persisted.hash !== source.hash || persisted.size !== source.size) { + throw new Error(`${options.label} target could not be verified`); + } + return persisted; +} + +// domains/comet-native/native-paths.ts +import { promises as fs5 } from "fs"; +import path5 from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs5.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs5.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +function inside(parent, target) { + const relative = path5.relative(parent, target); + return relative === "" || !path5.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path5.sep}`); +} +async function physicalPath(target) { + const missing = []; + let cursor = target; + while (!await isFileOrDirectory(cursor)) { + const parent = path5.dirname(cursor); + if (parent === cursor) break; + missing.push(path5.basename(cursor)); + cursor = parent; + } + const existing = await fs5.realpath(cursor); + return path5.resolve(existing, ...missing.reverse()); +} +async function isSymbolicLink(target) { + try { + return (await fs5.lstat(target)).isSymbolicLink(); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path5.resolve(startPath); + try { + if (!(await fs5.stat(cursor)).isDirectory()) cursor = path5.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path5.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path5.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path5.join(cursor, ".git"))) return cursor; + const parent = path5.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path5.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized = path5.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized === ".." || normalized.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized === "" ? "." : normalized; +} +async function resolveArtifactRoot(projectRoot, value) { + const normalized = normalizeArtifactRootRef(value); + const lexical = path5.resolve(projectRoot, ...normalized.split("/")); + const physicalProject = await fs5.realpath(projectRoot); + const physicalTarget = await physicalPath(lexical); + if (!inside(physicalProject, physicalTarget)) { + throw new Error("native.artifact_root resolves outside the project root"); + } + return lexical; +} +async function nativeProjectPaths(projectRoot, artifactRootRef) { + const normalized = normalizeArtifactRootRef(artifactRootRef); + const artifactRoot = await resolveArtifactRoot(projectRoot, normalized); + const nativeRoot = path5.join(artifactRoot, "comet"); + if (await isSymbolicLink(nativeRoot)) { + throw new Error("The configured Native comet root must not be a symbolic link"); + } + const [physicalArtifactRoot, physicalNativeRoot] = await Promise.all([ + physicalPath(artifactRoot), + physicalPath(nativeRoot) + ]); + if (!inside(physicalArtifactRoot, physicalNativeRoot)) { + throw new Error("The configured Native comet root resolves outside its artifact root"); + } + return { + projectRoot: path5.resolve(projectRoot), + configFile: path5.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")), + artifactRoot, + artifactRootRef: normalized, + nativeRoot, + specsDir: path5.join(nativeRoot, "specs"), + changesDir: path5.join(nativeRoot, "changes"), + archiveDir: path5.join(nativeRoot, "archive"), + runtimeDir: path5.join(nativeRoot, "runtime"), + locksDir: path5.join(nativeRoot, "runtime", "locks"), + transactionsDir: path5.join(nativeRoot, "runtime", "transactions") + }; +} +async function ensureNativeDirectories(paths) { + const directories = [ + paths.specsDir, + paths.changesDir, + paths.archiveDir, + paths.locksDir, + paths.transactionsDir + ]; + await Promise.all( + directories.map(async (directory) => { + await resolveContainedNativePath(paths.nativeRoot, directory); + await fs5.mkdir(directory, { recursive: true }); + }) + ); +} +function isInsidePath(parent, target) { + return inside(path5.resolve(parent), path5.resolve(target)); +} +async function resolveContainedNativePath(root, target) { + const lexicalRoot = path5.resolve(root); + const lexicalTarget = path5.resolve(target); + if (!inside(lexicalRoot, lexicalTarget)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + if (await isSymbolicLink(lexicalRoot)) { + throw new Error(`Native root must not be a symbolic link: ${root}`); + } + const [physicalRoot, physicalTarget] = await Promise.all([ + physicalPath(lexicalRoot), + physicalPath(lexicalTarget) + ]); + if (!inside(physicalRoot, physicalTarget)) { + throw new Error(`Path resolves outside the Native root: ${target}`); + } + return lexicalTarget; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot2 = record(value, "native.snapshot"); + rejectUnknown(snapshot2, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot2.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot2.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot2.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot2.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot2.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot2 = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot: snapshot2, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +function defaultProjectConfig(artifactRoot = "docs", language = "en") { + return { + schema: "comet.project.v1", + default_workflow: "native", + ambient_resume: true, + native: { + artifact_root: normalizeArtifactRootRef(artifactRoot), + language, + clarification_mode: "sequential", + snapshot: { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] } + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs6.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} +async function existingConfigDocument(projectRoot) { + const file = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + try { + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) throw new Error(document.errors[0].message); + const value = document.toJS(); + if (value && typeof value === "object" && !Array.isArray(value)) { + return value; + } + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + return {}; +} +async function assertNoPendingNativeRootMove(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (config?.native.pending_root_move) { + throw new Error( + `Native root move ${config.native.pending_root_move.id} is incomplete; use comet native doctor --repair` + ); + } +} +async function writeProjectConfig(projectRoot, config) { + const validated = parseConfig({ + schema: config.schema, + default_workflow: config.default_workflow, + workflows: config.workflows ?? [config.default_workflow], + ambient_resume: config.ambient_resume, + native: { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot, + ...config.native.pending_root_move ? { + pending_root_move: { + id: config.native.pending_root_move.id, + from_artifact_root: config.native.pending_root_move.fromArtifactRoot, + to_artifact_root: config.native.pending_root_move.toArtifactRoot, + stage: config.native.pending_root_move.stage, + ...config.native.pending_root_move.cleanup ? { + cleanup: { + kind: config.native.pending_root_move.cleanup.kind, + state: config.native.pending_root_move.cleanup.state, + manifest_hash: config.native.pending_root_move.cleanup.manifestHash + } + } : {} + } + } : {} + } + }); + const document = { + ...await existingConfigDocument(projectRoot), + schema: validated.schema, + default_workflow: validated.default_workflow, + workflows: validated.workflows, + ambient_resume: validated.ambient_resume, + native: { + artifact_root: validated.native.artifact_root, + language: validated.native.language, + clarification_mode: validated.native.clarification_mode, + snapshot: validated.native.snapshot, + ...validated.native.pending_root_move ? { + pending_root_move: { + id: validated.native.pending_root_move.id, + from_artifact_root: validated.native.pending_root_move.fromArtifactRoot, + to_artifact_root: validated.native.pending_root_move.toArtifactRoot, + stage: validated.native.pending_root_move.stage, + ...validated.native.pending_root_move.cleanup ? { + cleanup: { + kind: validated.native.pending_root_move.cleanup.kind, + state: validated.native.pending_root_move.cleanup.state, + manifest_hash: validated.native.pending_root_move.cleanup.manifestHash + } + } : {} + } + } : {} + } + }; + const canonical = path6.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + await fs6.mkdir(path6.dirname(canonical), { recursive: true }); + await atomicWriteText( + canonical, + renderStructuredProjectConfig(document, validated.native.language === "zh-CN" ? "zh-CN" : "en") + ); +} +async function resolveNativeProject(options) { + const projectRoot = await discoverNativeProject(options.startPath); + const existing = await readProjectConfig(projectRoot); + if (!existing && options.allowMissingConfig === false) { + throw new Error(`${PROJECT_CONFIG_FILE} was not found`); + } + if (existing?.native.pending_root_move) { + throw new Error( + `Native root move ${existing.native.pending_root_move.id} is incomplete; use comet native doctor --repair` + ); + } + const explicit = options.explicitArtifactRoot ? normalizeArtifactRootRef(options.explicitArtifactRoot) : void 0; + if (existing && explicit && explicit !== existing.native.artifact_root) { + throw new Error( + `Configured Native artifact root is ${existing.native.artifact_root}; refusing conflicting root ${explicit}` + ); + } + const config = existing ?? defaultProjectConfig(explicit ?? "docs"); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + return { config, paths, configured: existing !== null }; +} + +// domains/comet-native/native-mutation-lock.ts +import { promises as fs9 } from "fs"; +import path9 from "path"; + +// domains/comet-native/native-lock.ts +import { AsyncLocalStorage } from "async_hooks"; +import { randomUUID as randomUUID2 } from "crypto"; +import { promises as fs7 } from "fs"; +import os2 from "os"; +import path7 from "path"; +var NATIVE_LOCK_MAX_BYTES = 16 * 1024; +var NATIVE_LOCK_COORDINATOR_DIR = ".coordinator"; +var NATIVE_LOCK_COORDINATOR_TIMEOUT_MS = 5e3; +var nativeLockCoordinator = new AsyncLocalStorage(); +var nativeLockLocalCoordinator = /* @__PURE__ */ new Map(); +function lockName(value) { + if (!/^[a-z][a-z0-9-]*$/u.test(value)) throw new Error(`Invalid Native lock name: ${value}`); + return `${value}.lock`; +} +function parseNativeLockOwner(value, file) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Invalid Native lock metadata: ${file}`); + } + const owner = value; + if (typeof owner.id !== "string" || owner.id.length === 0 || typeof owner.pid !== "number" || !Number.isSafeInteger(owner.pid) || owner.pid < 1 || typeof owner.hostname !== "string" || owner.hostname.length === 0 || typeof owner.createdAt !== "string" || owner.createdAt.length === 0 || typeof owner.operation !== "string" || owner.operation.length === 0) { + throw new Error(`Invalid Native lock metadata: ${file}`); + } + return owner; +} +function nativeLockFileIdentity(stat) { + return { + device: stat.dev.toString(), + inode: stat.ino.toString(), + size: stat.size.toString(), + birthtimeNs: stat.birthtimeNs.toString(), + ctimeNs: stat.ctimeNs.toString(), + mtimeNs: stat.mtimeNs.toString() + }; +} +function sameNativeLockObject(left, right) { + const leftObject = { dev: left.device, ino: left.inode, birthtime: left.birthtimeNs }; + const rightObject = { dev: right.device, ino: right.inode, birthtime: right.birthtimeNs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.size === right.size; +} +function sameNativeLockVersion(left, right) { + return sameNativeLockObject(left, right) && left.size === right.size && left.birthtimeNs === right.birthtimeNs && left.ctimeNs === right.ctimeNs && left.mtimeNs === right.mtimeNs; +} +function sameNativeLockDiagnosis(left, right) { + if (left.status !== right.status) return false; + if (!left.owner || !left.identity || !right.owner || !right.identity) { + return left.owner === right.owner && left.identity === right.identity; + } + return left.owner.id === right.owner.id && sameNativeLockVersion(left.identity, right.identity); +} +async function readNativeLockSnapshot(file) { + let bytes; + let stat; + try { + const result2 = await readFileRaceSafe(file, NATIVE_LOCK_MAX_BYTES, { + bigint: true, + label: "Native lock" + }); + bytes = result2.bytes; + stat = result2.stat; + } catch (error) { + if (error.code === "ENOENT") return null; + if (error instanceof RaceSafeReadError) { + if (error.reason === "too-large") { + throw new Error(`Native lock metadata exceeds ${NATIVE_LOCK_MAX_BYTES} bytes: ${file}`, { + cause: error + }); + } + if (error.reason === "not-regular-file") { + throw new Error(`Native lock must be a regular file: ${file}`, { cause: error }); + } + throw new Error(`Native lock changed while reading: ${file}`, { cause: error }); + } + throw error; + } + return { + file, + owner: parseNativeLockOwner(JSON.parse(bytes.toString("utf8")), file), + identity: nativeLockFileIdentity(stat) + }; +} +async function readNativeLock(file) { + return (await readNativeLockSnapshot(file))?.owner ?? null; +} +function diagnosisFromSnapshot(snapshot2) { + if (!snapshot2) return { status: "missing", owner: null, identity: null }; + if (snapshot2.owner.hostname !== os2.hostname()) { + return { status: "unknown", owner: snapshot2.owner, identity: snapshot2.identity }; + } + const alive = isProcessAlive(snapshot2.owner.pid); + return { + status: alive === true ? "active" : alive === false ? "stale" : "unknown", + owner: snapshot2.owner, + identity: snapshot2.identity + }; +} +async function restoreQuarantinedNativeLock(quarantine, file) { + try { + await fs7.lstat(file); + return; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + try { + await fs7.rename(quarantine, file); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } +} +async function removeBoundNativeLock(expected, quarantineDir) { + const current = await readNativeLockSnapshot(expected.file); + if (!current) return "missing"; + if (current.owner.id !== expected.owner.id) { + throw new Error(`Native lock ownership changed: ${expected.file}`); + } + if (!sameNativeLockVersion(current.identity, expected.identity)) { + throw new Error(`Native lock identity changed: ${expected.file}`); + } + await fs7.mkdir(quarantineDir, { recursive: true }); + const quarantine = path7.join( + quarantineDir, + `${path7.basename(expected.file)}.${expected.owner.id}.${randomUUID2()}.removed` + ); + try { + await fs7.rename(expected.file, quarantine); + } catch (error) { + if (error.code === "ENOENT") return "missing"; + throw error; + } + const moved = await readNativeLockSnapshot(quarantine); + if (!moved || moved.owner.id !== expected.owner.id || !sameNativeLockObject(moved.identity, expected.identity)) { + await restoreQuarantinedNativeLock(quarantine, expected.file); + throw new Error(`Native lock changed before quarantine: ${expected.file}`); + } + await fs7.rm(quarantine, { force: true }); + return "removed"; +} +function newNativeLockOwner(operation) { + return { + id: randomUUID2(), + pid: process.pid, + hostname: os2.hostname(), + createdAt: (/* @__PURE__ */ new Date()).toISOString(), + operation + }; +} +async function writeNativeLockFile(file, owner) { + let handle; + try { + handle = await fs7.open(file, "wx"); + } catch (error) { + if (error.code === "EEXIST") { + const existing = await readNativeLock(file); + throw new Error( + `Native lock is already held: ${file}${existing ? ` by pid ${existing.pid} for ${existing.operation}` : ""}`, + { cause: error } + ); + } + throw error; + } + try { + await handle.writeFile(JSON.stringify(owner, null, 2) + "\n", "utf8"); + await handle.sync(); + return nativeLockFileIdentity(await handle.stat({ bigint: true })); + } finally { + await handle.close(); + } +} +async function publishNativeCoordinatorClaim(paths, operation) { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + await fs7.mkdir(locksDir, { recursive: true }); + const coordinatorDir = await resolveContainedNativePath( + paths.nativeRoot, + path7.join(locksDir, NATIVE_LOCK_COORDINATOR_DIR) + ); + await fs7.mkdir(coordinatorDir, { recursive: true }); + const owner = newNativeLockOwner(operation); + const temporary = path7.join(coordinatorDir, `.${owner.id}.tmp`); + const file = path7.join(coordinatorDir, `${owner.id}.claim`); + try { + const identity = await writeNativeLockFile(temporary, owner); + await fs7.rename(temporary, file); + const published = await readNativeLockSnapshot(file); + if (!published || !sameNativeLockObject(identity, published.identity)) { + throw new Error(`Native lock coordinator claim changed while publishing: ${file}`); + } + return { file, nativeRoot: paths.nativeRoot, locksDir, owner, identity: published.identity }; + } finally { + await fs7.rm(temporary, { force: true }); + } +} +async function hasNativeCoordinatorPredecessor(claim) { + const coordinatorDir = path7.dirname(claim.file); + let predecessor = false; + const claimName = path7.basename(claim.file); + for (const entry2 of await fs7.readdir(coordinatorDir, { withFileTypes: true })) { + if (!entry2.isFile() || entry2.isSymbolicLink() || !entry2.name.endsWith(".claim")) continue; + const file = path7.join(coordinatorDir, entry2.name); + if (path7.resolve(file) === path7.resolve(claim.file)) continue; + try { + const snapshot2 = await readNativeLockSnapshot(file); + const diagnosis = diagnosisFromSnapshot(snapshot2); + if (diagnosis.status === "missing") continue; + if (diagnosis.status === "stale" && snapshot2) { + await removeBoundNativeLock(snapshot2, coordinatorDir); + continue; + } + if (entry2.name < claimName) predecessor = true; + } catch { + if (entry2.name < claimName) predecessor = true; + } + } + return predecessor; +} +async function releaseNativeCoordinatorClaim(claim) { + const current = await readNativeLockSnapshot(claim.file); + if (!current) return; + if (current.owner.id !== claim.owner.id || !sameNativeLockVersion(current.identity, claim.identity)) { + throw new Error(`Native lock coordinator ownership changed: ${claim.file}`); + } + await removeBoundNativeLock(current, path7.dirname(claim.file)); +} +async function acquireNativeCoordinator(paths, operation) { + const deadline = Date.now() + NATIVE_LOCK_COORDINATOR_TIMEOUT_MS; + while (true) { + const claim = await publishNativeCoordinatorClaim(paths, operation); + if (!await hasNativeCoordinatorPredecessor(claim)) return claim; + await releaseNativeCoordinatorClaim(claim); + if (Date.now() >= deadline) { + throw new Error(`Native lock coordinator is busy: ${paths.locksDir}`); + } + await new Promise((resolve) => setTimeout(resolve, 2 + Math.floor(Math.random() * 7))); + } +} +async function acquireNativeLocalCoordinator(key) { + const previous = nativeLockLocalCoordinator.get(key) ?? Promise.resolve(); + let release; + const current = new Promise((resolve) => { + release = resolve; + }); + const turn = previous.then(() => current); + nativeLockLocalCoordinator.set(key, turn); + await previous; + return () => { + release(); + if (nativeLockLocalCoordinator.get(key) === turn) nativeLockLocalCoordinator.delete(key); + }; +} +async function withNativeLockCoordinator(paths, operation, work) { + const key = path7.resolve(paths.locksDir); + const current = nativeLockCoordinator.getStore(); + if (current?.has(key)) return work(); + const releaseLocal = await acquireNativeLocalCoordinator(key); + try { + const claim = await acquireNativeCoordinator(paths, operation); + const next = new Map(current ?? []); + next.set(key, claim); + return await nativeLockCoordinator.run(next, async () => { + try { + return await work(); + } finally { + await releaseNativeCoordinatorClaim(claim); + } + }); + } finally { + releaseLocal(); + } +} +async function withNativeLockRecovery(pathEntries, operation, work) { + const unique = [ + ...new Map(pathEntries.map((entry2) => [path7.resolve(entry2.locksDir), entry2])).values() + ].sort((left, right) => path7.resolve(left.locksDir).localeCompare(path7.resolve(right.locksDir))); + const enter = async (index) => { + const entry2 = unique[index]; + return entry2 ? withNativeLockCoordinator(entry2, operation, () => enter(index + 1)) : work(); + }; + return enter(0); +} +async function acquireNativeLock(paths, name, operation) { + return withNativeLockCoordinator(paths, `acquire ${name}`, async () => { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + await fs7.mkdir(locksDir, { recursive: true }); + const file = await resolveContainedNativePath( + paths.nativeRoot, + path7.join(locksDir, lockName(name)) + ); + const owner = newNativeLockOwner(operation); + const identity = await writeNativeLockFile(file, owner); + return { file, nativeRoot: paths.nativeRoot, locksDir, owner, identity }; + }); +} +async function releaseNativeLock(lock) { + if (!await readNativeLockSnapshot(lock.file)) return; + await withNativeLockCoordinator(lock, `release ${path7.basename(lock.file)}`, async () => { + const current = await readNativeLockSnapshot(lock.file); + if (!current) return; + if (current.owner.id !== lock.owner.id) { + throw new Error(`Native lock ownership changed: ${lock.file}`); + } + if (!sameNativeLockVersion(current.identity, lock.identity)) { + throw new Error(`Native lock identity changed: ${lock.file}`); + } + const coordinatorDir = path7.join(lock.locksDir, NATIVE_LOCK_COORDINATOR_DIR); + await removeBoundNativeLock(current, coordinatorDir); + }); +} +function isProcessAlive(pid) { + try { + process.kill(pid, 0); + return true; + } catch (error) { + const code = error.code; + if (code === "ESRCH") return false; + if (code === "EPERM") return true; + return null; + } +} +async function diagnoseNativeLock(file) { + return diagnosisFromSnapshot(await readNativeLockSnapshot(file)); +} +async function takeOverNativeStaleLock(paths, file, expected) { + return withNativeLockCoordinator(paths, `take over ${path7.basename(file)}`, async () => { + const locksDir = await resolveContainedNativePath(paths.nativeRoot, paths.locksDir); + const containedFile = await resolveContainedNativePath(paths.nativeRoot, file); + if (path7.resolve(path7.dirname(containedFile)) !== path7.resolve(locksDir)) { + throw new Error(`Native lock takeover target is outside the lock directory: ${file}`); + } + const snapshot2 = await readNativeLockSnapshot(containedFile); + const diagnosis = diagnosisFromSnapshot(snapshot2); + if (diagnosis.status === "missing") return { status: "missing" }; + if (expected && !sameNativeLockDiagnosis(expected, diagnosis)) { + return { status: "changed", diagnosis }; + } + if (diagnosis.status !== "stale" || !snapshot2) { + return { status: "changed", diagnosis }; + } + const coordinatorDir = path7.join(locksDir, NATIVE_LOCK_COORDINATOR_DIR); + const removed = await removeBoundNativeLock(snapshot2, coordinatorDir); + return removed === "removed" ? { status: "removed", owner: snapshot2.owner } : { status: "missing" }; + }); +} + +// domains/comet-native/native-transaction.ts +import { promises as fs8 } from "fs"; +import path8 from "path"; +import { TextDecoder as TextDecoder3 } from "util"; +var JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "kind", + "status", + "projectRoot", + "nativeRoot", + "change", + "createdAt", + "operations" +]); +var OPERATION_KEYS = /* @__PURE__ */ new Set(["id", "type", "source", "target", "staged", "backup"]); +var ARCHIVE_V2_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "kind", + "status", + "change", + "createdAt", + "preflightHash", + "operations" +]); +var ARCHIVE_V2_OPERATION_KEYS = /* @__PURE__ */ new Set([ + "id", + "type", + "source", + "target", + "staged", + "backup", + "expectedSourceHash", + "expectedTargetHash", + "stagedHash" +]); +var EVENT_KEYS = /* @__PURE__ */ new Set(["sequence", "timestamp", "type", "operationId"]); +var TRANSACTION_STATUSES = /* @__PURE__ */ new Set([ + "prepared", + "applying", + "committed", + "rolling-back", + "rolled-back" +]); +var EVENT_TYPES = /* @__PURE__ */ new Set([ + "prepared", + "operation-started", + "operation-completed", + "archive-finalization-started", + "archive-finalized", + "commit", + "rollback-started", + "rollback-completed" +]); +var NATIVE_TRANSACTION_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_TRANSACTION_EVENTS_MAX_BYTES = 1024 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_BYTES = 16 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_COUNT = 1024; +var NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES = 64 * 1024 * 1024; +var NATIVE_LEGACY_TRANSACTION_DIRECTORY_MAX_ENTRIES = 2e4; +var UTF8_DECODER = new TextDecoder3("utf-8", { fatal: true }); +function record2(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function rejectUnknown2(value, keys, label) { + const unknown = Object.keys(value).filter((key) => !keys.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function validTimestamp(value) { + if (typeof value !== "string") return false; + const parsed = new Date(value); + return !Number.isNaN(parsed.valueOf()) && parsed.toISOString() === value; +} +function assertRef(ref, label) { + if (typeof ref !== "string" || ref.length === 0 || path8.isAbsolute(ref) || /^(?:[A-Za-z]:|~|[\\/])/u.test(ref) || ref.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native root`); + } +} +function assertHash(value, label) { + if (typeof value !== "string" || !/^[a-f0-9]{64}$/u.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } +} +function assertArchiveRef(ref, label) { + assertRef(ref, label); + if (ref.includes("\\") || ref !== path8.posix.normalize(ref) || ref.split("/").includes(".") || ref.endsWith("/") || Buffer.byteLength(ref, "utf8") > 1024) { + throw new Error(`${label} must be a normalized Native-relative ref`); + } +} +function parseOperation(value, index) { + const operation = record2(value, `transaction operations[${index}]`); + rejectUnknown2(operation, OPERATION_KEYS, `transaction operations[${index}]`); + if (typeof operation.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(operation.id)) { + throw new Error(`transaction operations[${index}].id is invalid`); + } + if (operation.type !== "write" && operation.type !== "remove" && operation.type !== "move") { + throw new Error(`transaction operation ${operation.id} has an invalid type`); + } + assertRef(operation.target, `transaction operation ${operation.id} target`); + for (const field2 of ["source", "staged", "backup"]) { + if (operation[field2] !== void 0) { + assertRef(operation[field2], `transaction operation ${operation.id} ${field2}`); + } + } + if (operation.type === "write") { + if (operation.staged === void 0 || operation.source !== void 0) { + throw new Error(`write operation ${operation.id} requires staged and forbids source`); + } + } else if (operation.type === "remove") { + if (operation.source !== void 0 || operation.staged !== void 0) { + throw new Error(`remove operation ${operation.id} forbids source and staged`); + } + } else if (operation.source === void 0 || operation.staged !== void 0 || operation.backup !== void 0) { + throw new Error(`move operation ${operation.id} requires source and forbids staged and backup`); + } + return operation; +} +function parseNativeArchiveTransactionJournalV2(value) { + const journal = record2(value, "Native Archive transaction journal"); + rejectUnknown2(journal, ARCHIVE_V2_JOURNAL_KEYS, "Native Archive transaction journal"); + if (journal.schema !== "comet.native.transaction.v2") { + throw new Error("Unsupported Native Archive transaction schema"); + } + if (typeof journal.id !== "string" || !/^[a-f0-9-]{8,}$/u.test(journal.id)) { + throw new Error("Native Archive transaction id is invalid"); + } + if (journal.kind !== "archive") throw new Error("Native v2 transaction kind must be archive"); + if (typeof journal.status !== "string" || !TRANSACTION_STATUSES.has(journal.status)) { + throw new Error("Native Archive transaction status is invalid"); + } + if (typeof journal.change !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(journal.change)) { + throw new Error("Native Archive transaction change name is invalid"); + } + if (!validTimestamp(journal.createdAt)) { + throw new Error("Native Archive transaction createdAt is invalid"); + } + assertHash(journal.preflightHash, "Native Archive transaction preflightHash"); + if (!Array.isArray(journal.operations) || journal.operations.length > 65) { + throw new Error("Native Archive transaction operations must be an array"); + } + const operations = journal.operations.map((value2, index) => { + const operation = record2(value2, `Archive transaction operations[${index}]`); + rejectUnknown2(operation, ARCHIVE_V2_OPERATION_KEYS, `Archive transaction operations[${index}]`); + if (typeof operation.id !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(operation.id)) { + throw new Error(`Archive transaction operations[${index}].id is invalid`); + } + if (operation.type !== "write" && operation.type !== "remove" && operation.type !== "move") { + throw new Error(`Archive transaction operation ${operation.id} has an invalid type`); + } + assertArchiveRef(operation.target, `Archive transaction operation ${operation.id} target`); + for (const field2 of ["source", "staged", "backup"]) { + if (operation[field2] !== void 0) { + assertArchiveRef( + operation[field2], + `Archive transaction operation ${operation.id} ${field2}` + ); + } + } + if (operation.expectedTargetHash !== null) { + assertHash( + operation.expectedTargetHash, + `Archive transaction operation ${operation.id} expectedTargetHash` + ); + } + if (operation.type === "write") { + if (operation.staged === void 0 || operation.source !== void 0 || operation.expectedSourceHash !== void 0) { + throw new Error( + `Archive write operation ${operation.id} requires staged and forbids source` + ); + } + assertHash(operation.stagedHash, `Archive write operation ${operation.id} stagedHash`); + if (operation.expectedTargetHash === null !== (operation.backup === void 0)) { + throw new Error( + `Archive write operation ${operation.id} backup must match target existence` + ); + } + } else if (operation.type === "remove") { + if (operation.source !== void 0 || operation.staged !== void 0 || operation.stagedHash !== void 0 || operation.expectedSourceHash !== void 0 || operation.backup === void 0 || operation.expectedTargetHash === null) { + throw new Error( + `Archive remove operation ${operation.id} requires a bound target and backup` + ); + } + } else { + if (operation.source === void 0 || operation.staged !== void 0 || operation.stagedHash !== void 0 || operation.backup !== void 0 || operation.expectedTargetHash !== null) { + throw new Error( + `Archive move operation ${operation.id} requires source and an absent target` + ); + } + assertHash( + operation.expectedSourceHash, + `Archive move operation ${operation.id} expectedSourceHash` + ); + } + return operation; + }); + const operationIds = operations.map((operation) => operation.id); + if (new Set(operationIds).size !== operationIds.length) { + throw new Error("Native Archive transaction operation ids must be unique"); + } + const transactionPrefix = `runtime/transactions/${journal.id}`; + const archiveMoves = operations.filter((operation) => operation.type === "move"); + if (archiveMoves.length !== 1 || archiveMoves[0].id !== "archive-change" || archiveMoves[0].source !== `changes/${journal.change}` || !new RegExp(`^archive/\\d{4}-\\d{2}-\\d{2}-${journal.change}$`, "u").test( + archiveMoves[0].target + ) || operations.at(-1) !== archiveMoves[0]) { + throw new Error("Native Archive transaction must end with its exact change move"); + } + const specTargets = /* @__PURE__ */ new Set(); + for (const operation of operations.slice(0, -1)) { + if (operation.type === "move" || !/^specs\/[a-z][a-z0-9]*(?:-[a-z0-9]+)*\/spec\.md$/u.test(operation.target) || specTargets.has(operation.target)) { + throw new Error(`Native Archive transaction spec target is invalid: ${operation.target}`); + } + specTargets.add(operation.target); + if (operation.staged !== void 0 && !operation.staged.startsWith(`${transactionPrefix}/staged/specs/`)) { + throw new Error(`Native Archive transaction staged ref is invalid: ${operation.staged}`); + } + if (operation.backup !== void 0 && !operation.backup.startsWith(`${transactionPrefix}/backups/specs/`)) { + throw new Error(`Native Archive transaction backup ref is invalid: ${operation.backup}`); + } + } + return { + schema: "comet.native.transaction.v2", + id: journal.id, + kind: "archive", + status: journal.status, + change: journal.change, + createdAt: journal.createdAt, + preflightHash: journal.preflightHash, + operations + }; +} +function parseJournal(value) { + const journal = record2(value, "Native transaction journal"); + if (journal.schema === "comet.native.transaction.v2") { + return parseNativeArchiveTransactionJournalV2(journal); + } + rejectUnknown2(journal, JOURNAL_KEYS, "Native transaction journal"); + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Unsupported Native transaction schema"); + } + if (typeof journal.id !== "string" || !/^[a-f0-9-]{8,}$/u.test(journal.id)) { + throw new Error("Native transaction id is invalid"); + } + if (journal.kind !== "archive" && journal.kind !== "root-move") { + throw new Error("Native transaction kind is invalid"); + } + if (typeof journal.status !== "string" || !TRANSACTION_STATUSES.has(journal.status)) { + throw new Error("Native transaction status is invalid"); + } + if (typeof journal.projectRoot !== "string" || !path8.isAbsolute(journal.projectRoot) || typeof journal.nativeRoot !== "string" || !path8.isAbsolute(journal.nativeRoot)) { + throw new Error("Native transaction roots must be absolute paths"); + } + if (journal.change !== void 0 && (typeof journal.change !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(journal.change))) { + throw new Error("Native transaction change name is invalid"); + } + if (!validTimestamp(journal.createdAt)) { + throw new Error("Native transaction createdAt is invalid"); + } + if (!Array.isArray(journal.operations)) { + throw new Error("Native transaction operations must be an array"); + } + const operations = journal.operations.map(parseOperation); + const operationIds = operations.map((operation) => operation.id); + if (new Set(operationIds).size !== operationIds.length) { + throw new Error("Native transaction operation ids must be unique"); + } + return { + schema: "comet.native.transaction.v1", + id: journal.id, + kind: journal.kind, + status: journal.status, + projectRoot: journal.projectRoot, + nativeRoot: journal.nativeRoot, + ...typeof journal.change === "string" ? { change: journal.change } : {}, + createdAt: journal.createdAt, + operations + }; +} +function parseEvent(value, line) { + const event = record2(value, `Native transaction event at line ${line}`); + rejectUnknown2(event, EVENT_KEYS, `Native transaction event at line ${line}`); + if (event.sequence !== line) { + throw new Error(`Native transaction event sequence at line ${line} must be ${line}`); + } + if (!validTimestamp(event.timestamp)) { + throw new Error(`Native transaction event timestamp at line ${line} is invalid`); + } + if (typeof event.type !== "string" || !EVENT_TYPES.has(event.type)) { + throw new Error(`Native transaction event type at line ${line} is invalid`); + } + const operationEvent = event.type === "operation-started" || event.type === "operation-completed"; + if (operationEvent && (typeof event.operationId !== "string" || !/^[a-z0-9][a-z0-9-]*$/u.test(event.operationId) || Buffer.byteLength(event.operationId, "utf8") > 256) || !operationEvent && event.operationId !== void 0) { + throw new Error(`Native transaction event operationId at line ${line} is invalid`); + } + return { + sequence: event.sequence, + timestamp: event.timestamp, + type: event.type, + ...typeof event.operationId === "string" ? { operationId: event.operationId } : {} + }; +} +function matchesIsoTimestampPrefix(value) { + if (value.length > 24) return false; + const shape = "0000-00-00T00:00:00.000Z"; + for (let index = 0; index < value.length; index += 1) { + const expected = shape[index]; + const actual = value[index]; + if (expected === "0") { + if (!/[0-9]/u.test(actual)) return false; + } else if (actual !== expected) { + return false; + } + } + const numericRanges = [ + [5, 2, 1, 12], + [8, 2, 1, 31], + [11, 2, 0, 23], + [14, 2, 0, 59], + [17, 2, 0, 59], + [20, 3, 0, 999] + ]; + for (const [offset, width, minimum, maximum] of numericRanges) { + if (value.length <= offset) continue; + const partial = value.slice(offset, Math.min(offset + width, value.length)); + const completable = Array.from( + { length: maximum - minimum + 1 }, + (_entry, index) => String(minimum + index).padStart(width, "0") + ).some((candidate) => candidate.startsWith(partial)); + if (!completable) return false; + } + if (value.length >= 10 && !validTimestamp(`${value.slice(0, 10)}T00:00:00.000Z`)) { + return false; + } + return value.length < 24 || validTimestamp(value); +} +function isStrictLiteralPrefix(value, expected) { + return value.length < expected.length && expected.startsWith(value); +} +function couldCompleteOperationEventSuffix(value) { + const operationPrefix = '","operationId":"'; + if (isStrictLiteralPrefix(value, operationPrefix)) return true; + if (!value.startsWith(operationPrefix)) return false; + const remainder = value.slice(operationPrefix.length); + const closingQuote = remainder.indexOf('"'); + if (closingQuote === -1) { + return remainder.length === 0 || /^[a-z0-9][a-z0-9-]*$/u.test(remainder); + } + const operationId = remainder.slice(0, closingQuote); + if (!/^[a-z0-9][a-z0-9-]*$/u.test(operationId)) return false; + return isStrictLiteralPrefix(remainder.slice(closingQuote), '"}'); +} +function isRecognizedIncompleteEventTail(source, sequence) { + if (source.length === 0 || Buffer.byteLength(source, "utf8") > NATIVE_TRANSACTION_EVENT_MAX_BYTES) { + return false; + } + const prefix = `{"sequence":${sequence},"timestamp":"`; + if (isStrictLiteralPrefix(source, prefix)) return true; + if (!source.startsWith(prefix)) return false; + const afterPrefix = source.slice(prefix.length); + const timestamp2 = afterPrefix.slice(0, Math.min(24, afterPrefix.length)); + if (!matchesIsoTimestampPrefix(timestamp2)) return false; + if (afterPrefix.length < 24) return true; + if (!validTimestamp(timestamp2)) return false; + const typePrefix = '","type":"'; + const afterTimestamp = afterPrefix.slice(24); + if (isStrictLiteralPrefix(afterTimestamp, typePrefix)) return true; + if (!afterTimestamp.startsWith(typePrefix)) return false; + const typeAndSuffix = afterTimestamp.slice(typePrefix.length); + for (const type of EVENT_TYPES) { + if (isStrictLiteralPrefix(typeAndSuffix, type)) return true; + if (!typeAndSuffix.startsWith(type)) continue; + const suffix = typeAndSuffix.slice(type.length); + if (type === "operation-started" || type === "operation-completed") { + if (couldCompleteOperationEventSuffix(suffix)) return true; + } else if (isStrictLiteralPrefix(suffix, '"}')) { + return true; + } + } + return false; +} +function parseEventLine(source, line) { + if (source.length === 0) throw new Error("Blank transaction event line"); + if (Buffer.byteLength(source, "utf8") > NATIVE_TRANSACTION_EVENT_MAX_BYTES) { + throw new Error(`Native transaction event at line ${line} is too large`); + } + return parseEvent(JSON.parse(source), line); +} +function parseEventLogSource(source) { + const entries = source.split("\n"); + const terminated = entries.at(-1) === ""; + if (terminated) entries.pop(); + if (entries.length > NATIVE_TRANSACTION_EVENT_MAX_COUNT) { + throw new Error( + `Native transaction event log exceeds ${NATIVE_TRANSACTION_EVENT_MAX_COUNT} events` + ); + } + const events = []; + for (let index = 0; index < entries.length; index += 1) { + const line = index + 1; + const raw = entries[index]; + const entry2 = raw.endsWith("\r") ? raw.slice(0, -1) : raw; + const finalUnterminated = !terminated && index === entries.length - 1; + try { + events.push(parseEventLine(entry2, line)); + } catch (error) { + if (finalUnterminated) { + let syntacticallyComplete = false; + try { + JSON.parse(entry2); + syntacticallyComplete = true; + } catch { + } + if (!syntacticallyComplete && isRecognizedIncompleteEventTail(entry2, line)) break; + } + throw new Error(`Invalid Native transaction event at line ${line}`, { cause: error }); + } + } + return events; +} +function canonicalEventLogSource(events) { + return events.length === 0 ? "" : `${events.map((event) => JSON.stringify(event)).join("\n")} +`; +} +function transactionDir(paths, id) { + if (!/^[a-f0-9-]{8,}$/u.test(id)) throw new Error(`Invalid Native transaction id: ${id}`); + return path8.join(paths.transactionsDir, id); +} +function nativeTransactionPaths(paths, id) { + const directory = transactionDir(paths, id); + return { + directory, + journal: path8.join(directory, "transaction.json"), + events: path8.join(directory, "events.jsonl"), + staged: path8.join(directory, "staged"), + backups: path8.join(directory, "backups") + }; +} +async function resolveNativeTransactionPaths(paths, id) { + const transaction = nativeTransactionPaths(paths, id); + await Promise.all( + Object.values(transaction).map( + (target) => resolveContainedNativePath(paths.nativeRoot, target) + ) + ); + return transaction; +} +function resolveRefLexically(paths, ref) { + if (ref.length === 0 || path8.isAbsolute(ref) || /^(?:[A-Za-z]:|~|[\\/])/u.test(ref) || ref.split(/[\\/]/u).includes("..")) { + throw new Error(`Unsafe Native transaction ref: ${ref}`); + } + const target = path8.resolve(paths.nativeRoot, ...ref.split(/[\\/]/u)); + if (!isInsidePath(paths.nativeRoot, target)) + throw new Error(`Unsafe Native transaction ref: ${ref}`); + return target; +} +async function resolveRef(paths, ref) { + return resolveContainedNativePath(paths.nativeRoot, resolveRefLexically(paths, ref)); +} +async function exists(file) { + try { + await fs8.access(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function readEventLogSnapshot(paths, id, options = {}) { + const tx = await resolveNativeTransactionPaths(paths, id); + try { + await fs8.lstat(tx.events); + } catch (error) { + if (error.code === "ENOENT") { + return { + exists: false, + hash: null, + size: 0, + events: [], + canonicalSource: "", + needsRepair: false + }; + } + throw error; + } + try { + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.events, + maxBytes: NATIVE_TRANSACTION_EVENTS_MAX_BYTES, + label: `Native transaction event log ${id}`, + hooks: options.hooks + }); + let source; + try { + source = UTF8_DECODER.decode(snapshot2.bytes); + } catch (error) { + throw new Error(`Native transaction event log ${id} is not valid UTF-8`, { cause: error }); + } + const events = parseEventLogSource(source); + const canonicalSource = canonicalEventLogSource(events); + return { + exists: true, + hash: snapshot2.hash, + size: snapshot2.size, + events, + canonicalSource, + needsRepair: source !== canonicalSource + }; + } catch (error) { + if (error.code === "ENOENT") { + throw new Error(`Native transaction event log ${id} changed while reading`, { + cause: error + }); + } + throw error; + } +} +async function assertEventLogSnapshotUnchanged(paths, id, expected) { + const actual = await readEventLogSnapshot(paths, id); + if (actual.exists !== expected.exists || actual.hash !== expected.hash || actual.size !== expected.size) { + throw new Error(`Native transaction event log ${id} changed before append`); + } +} +async function appendNativeTransactionEvent(paths, id, type, operationId) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readEventLogSnapshot(paths, id); + const existing = snapshot2.events.find( + (event2) => event2.type === type && event2.operationId === operationId + ); + if (existing) { + if (snapshot2.needsRepair) { + await atomicWriteText(tx.events, snapshot2.canonicalSource, { + containedRoot: paths.nativeRoot, + beforeCommit: () => assertEventLogSnapshotUnchanged(paths, id, snapshot2) + }); + } + return existing; + } + if (snapshot2.events.length >= NATIVE_TRANSACTION_EVENT_MAX_COUNT) { + throw new Error( + `Native transaction event log ${id} exceeds ${NATIVE_TRANSACTION_EVENT_MAX_COUNT} events` + ); + } + const event = { + sequence: snapshot2.events.length + 1, + timestamp: (/* @__PURE__ */ new Date()).toISOString(), + type, + ...operationId ? { operationId } : {} + }; + parseEvent(event, event.sequence); + await fs8.mkdir(tx.directory, { recursive: true }); + await atomicWriteText(tx.events, canonicalEventLogSource([...snapshot2.events, event]), { + containedRoot: paths.nativeRoot, + beforeCommit: () => assertEventLogSnapshotUnchanged(paths, id, snapshot2) + }); + return event; +} +async function createNativeTransaction(paths, journal) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + journal = parseJournal(journal); + const tx = await resolveNativeTransactionPaths(paths, journal.id); + await fs8.mkdir(tx.staged, { recursive: true }); + await fs8.mkdir(tx.backups, { recursive: true }); + await atomicWriteJson(tx.journal, journal); + await appendNativeTransactionEvent(paths, journal.id, "prepared"); +} +async function readNativeTransaction(paths, id, options = {}) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.journal, + maxBytes: NATIVE_TRANSACTION_JOURNAL_MAX_BYTES, + label: `Native transaction journal ${id}`, + hooks: options.hooks + }); + const value = JSON.parse(UTF8_DECODER.decode(snapshot2.bytes)); + const journal = parseJournal(value); + if (journal.id !== id) { + throw new Error(`Invalid Native transaction journal: ${id}`); + } + return journal; +} +async function readNativeTransactionEvents(paths, id, options = {}) { + return (await readEventLogSnapshot(paths, id, options)).events; +} +async function setNativeTransactionStatus(paths, journal, status) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + const updated = parseJournal({ ...journal, status }); + await atomicWriteJson((await resolveNativeTransactionPaths(paths, journal.id)).journal, updated); + return updated; +} +async function readLegacyTransactionFile(paths, file, label) { + try { + return await readNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + label + }); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function copyAtomic(paths, source, target, label) { + const sourceSnapshot = await readLegacyTransactionFile(paths, source, `${label} source`); + if (!sourceSnapshot) throw new Error(`${label} source does not exist`); + const targetSnapshot = await readLegacyTransactionFile(paths, target, `${label} target`); + await ensureNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: path8.dirname(target), + label: `${label} target parent` + }); + await copyNativeProtectedFile({ + sourceRoot: paths.nativeRoot, + source, + targetRoot: paths.nativeRoot, + target, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + label, + expectedHash: sourceSnapshot.hash, + expectedTargetHash: targetSnapshot?.hash ?? null, + exclusive: targetSnapshot === null + }); +} +async function removeLegacyTransactionFile(paths, file, label) { + const snapshot2 = await readLegacyTransactionFile(paths, file, label); + if (!snapshot2) return; + await removeNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES, + expectedHash: snapshot2.hash, + expectedSize: snapshot2.size, + label + }); +} +async function backupTarget(paths, operation) { + if (!operation.backup) return; + const target = await resolveRef(paths, operation.target); + const backup = await resolveRef(paths, operation.backup); + if (!await exists(target) || await exists(backup)) return; + await copyAtomic(paths, target, backup, `Legacy Native transaction backup ${operation.id}`); +} +async function applyOperation(paths, operation) { + const target = await resolveRef(paths, operation.target); + if (operation.type === "write") { + if (!operation.staged) throw new Error(`Write operation ${operation.id} has no staged ref`); + await backupTarget(paths, operation); + await copyAtomic( + paths, + await resolveRef(paths, operation.staged), + target, + `Legacy Native transaction write ${operation.id}` + ); + return; + } + if (operation.type === "remove") { + await backupTarget(paths, operation); + await removeLegacyTransactionFile( + paths, + target, + `Legacy Native transaction remove ${operation.id}` + ); + return; + } + if (!operation.source) throw new Error(`Move operation ${operation.id} has no source ref`); + const source = await resolveRef(paths, operation.source); + const [sourceExists, targetExists] = await Promise.all([exists(source), exists(target)]); + if (!sourceExists && targetExists) { + const targetDirectory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: target, + label: `Legacy Native transaction move target ${operation.id}`, + maxEntries: NATIVE_LEGACY_TRANSACTION_DIRECTORY_MAX_ENTRIES + }); + await targetDirectory.verify(); + return; + } + if (targetExists) throw new Error(`Move target already exists: ${operation.target}`); + if (!sourceExists) throw new Error(`Move source does not exist: ${operation.source}`); + await moveNativeProtectedDirectory({ + root: paths.nativeRoot, + source, + target, + label: `Legacy Native transaction move ${operation.id}` + }); +} +async function applyNativeTransaction(paths, journal, hooks) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + let current = journal.status === "prepared" ? await setNativeTransactionStatus(paths, journal, "applying") : journal; + const events = await readNativeTransactionEvents(paths, journal.id); + const completed = new Set( + events.filter((event) => event.type === "operation-completed").map((event) => event.operationId) + ); + let completedCount = completed.size; + for (const operation of current.operations) { + if (completed.has(operation.id)) continue; + await appendNativeTransactionEvent(paths, current.id, "operation-started", operation.id); + await applyOperation(paths, operation); + await appendNativeTransactionEvent(paths, current.id, "operation-completed", operation.id); + completedCount += 1; + await hooks?.afterOperation?.(operation, completedCount); + } + current = await readNativeTransaction(paths, current.id); + return current; +} +async function rollbackOperation(paths, operation) { + const target = await resolveRef(paths, operation.target); + const backup = operation.backup ? await resolveRef(paths, operation.backup) : null; + if (operation.type === "move") { + if (!operation.source) throw new Error(`Move operation ${operation.id} has no source ref`); + const source = await resolveRef(paths, operation.source); + if (await exists(target)) { + if (await exists(source)) { + throw new Error(`Legacy Native rollback source already exists: ${operation.source}`); + } + await moveNativeProtectedDirectory({ + root: paths.nativeRoot, + source: target, + target: source, + label: `Legacy Native transaction rollback move ${operation.id}` + }); + } + return; + } + if (backup && await exists(backup)) { + await copyAtomic( + paths, + backup, + target, + `Legacy Native transaction rollback restore ${operation.id}` + ); + } else { + await removeLegacyTransactionFile( + paths, + target, + `Legacy Native transaction rollback remove ${operation.id}` + ); + } +} +async function rollbackNativeTransaction(paths, journal) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some( + (event) => event.type === "archive-finalization-started" || event.type === "archive-finalized" + )) { + throw new Error("An archive whose finalization started can only be recovered by continuing it"); + } + let current = await setNativeTransactionStatus(paths, journal, "rolling-back"); + await appendNativeTransactionEvent(paths, current.id, "rollback-started"); + const started = new Set( + events.filter((event) => event.type === "operation-started" || event.type === "operation-completed").map((event) => event.operationId) + ); + for (const operation of [...current.operations].reverse()) { + if (started.has(operation.id)) await rollbackOperation(paths, operation); + } + await appendNativeTransactionEvent(paths, current.id, "rollback-completed"); + current = await setNativeTransactionStatus(paths, current, "rolled-back"); + return current; +} +async function finalizeNativeTransaction(paths, journal, event) { + if (journal.schema !== "comet.native.transaction.v1") { + throw new Error("Native Archive v2 transactions require the content-bound transaction API"); + } + await appendNativeTransactionEvent(paths, journal.id, event); + return event === "commit" ? setNativeTransactionStatus(paths, journal, "committed") : journal; +} +function nativeRootRef(paths, target) { + const absolute = path8.resolve(target); + if (!isInsidePath(paths.nativeRoot, absolute)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + return path8.relative(paths.nativeRoot, absolute).split(path8.sep).join("/"); +} + +// domains/comet-native/native-mutation-lock.ts +async function hasUnfinishedTransaction(paths, allowedTransactionId) { + let entries; + try { + entries = await fs9.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + try { + const transaction = await readNativeTransaction(paths, entry2.name); + if (transaction.id !== allowedTransactionId && transaction.status !== "committed" && transaction.status !== "rolled-back") { + return true; + } + } catch { + return true; + } + } + return false; +} +async function acquireNativeMutationLock(paths, operation) { + const deadline = Date.now() + 5e3; + const file = path9.join(paths.locksDir, "root-move.lock"); + while (true) { + try { + return await acquireNativeLock(paths, "root-move", operation); + } catch (error) { + const cause = error.cause; + if (cause?.code !== "EEXIST") throw error; + const diagnosis = await diagnoseNativeLock(file); + if (diagnosis.status === "missing") continue; + if (diagnosis.status !== "active" || Date.now() >= deadline) throw error; + await new Promise((resolve) => setTimeout(resolve, 5 + Math.floor(Math.random() * 11))); + } + } +} +async function withNativeMutationLock(paths, operation, work, options) { + const lock = await acquireNativeMutationLock(paths, operation); + try { + await assertNoPendingNativeRootMove(paths.projectRoot); + if (await hasUnfinishedTransaction(paths, options?.allowedTransactionId)) { + throw new Error("Native transaction recovery is required before another mutation"); + } + return await work(); + } finally { + await releaseNativeLock(lock); + } +} + +// domains/comet-native/native-revision.ts +function validRevision(value) { + return Number.isSafeInteger(value) && value >= 1; +} +async function compareAndSwapNativeRevision(options) { + if (!validRevision(options.expectedRevision)) { + throw new Error("Native expected revision must be a positive integer"); + } + if (options.next.revision !== options.expectedRevision + 1) { + throw new Error("Native CAS next revision must increment the expected revision exactly once"); + } + const current = await options.read(); + if (!validRevision(current.revision)) { + throw new Error("Native current revision must be a positive integer"); + } + const equals = options.equals ?? ((left, right) => JSON.stringify(left) === JSON.stringify(right)); + if (current.revision === options.next.revision && equals(current, options.next)) { + return current; + } + if (current.revision !== options.expectedRevision) { + throw options.conflict(current.revision); + } + await options.write(options.next); + return options.next; +} + +// domains/comet-native/native-snapshot.ts +import { createHash as createHash5 } from "crypto"; +import { spawn } from "node:child_process"; +import { promises as fs10 } from "fs"; +import path10 from "path"; + +// domains/comet-native/native-hash.ts +import { createHash as createHash4 } from "crypto"; +import { createReadStream } from "fs"; +async function sha256File(file) { + const hash6 = createHash4("sha256"); + for await (const chunk of createReadStream(file)) hash6.update(chunk); + return hash6.digest("hex"); +} +function sha256Text(content) { + return createHash4("sha256").update(content).digest("hex"); +} + +// domains/comet-native/native-snapshot.ts +var DEFAULT_NATIVE_SNAPSHOT_LIMITS = { + maxFiles: 1e4, + maxFileBytes: 5 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxManifestBytes: 1024 * 1024 +}; +var MAX_RECORDED_OMISSIONS = 1e3; +var NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES = 8 * 1024 * 1024; +var CHANGE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var MANIFEST_KEYS = /* @__PURE__ */ new Set([ + "schema", + "origin", + "capture", + "createdAt", + "complete", + "limits", + "entries", + "omitted", + "omittedCount", + "omissionOverflow", + "policy" +]); +var LIMIT_KEYS = /* @__PURE__ */ new Set([ + "maxFiles", + "maxFileBytes", + "maxTotalBytes", + "maxManifestBytes", + "maxDurationMs" +]); +var POLICY_KEYS = /* @__PURE__ */ new Set(["schema", "include", "exclude", "hash"]); +var CAPTURE_KEYS = /* @__PURE__ */ new Set(["provider", "gitSelection", "physicalSelection", "projection"]); +var GIT_PROJECTION_KEYS = /* @__PURE__ */ new Set(["provider", "selection"]); +var GIT_SELECTION_KEYS = /* @__PURE__ */ new Set([ + "schema", + "status", + "stageBefore", + "combined", + "stageAfter", + "finalStageBefore", + "finalCombined", + "finalStageAfter" +]); +var GIT_SELECTION_STREAM_KEYS = /* @__PURE__ */ new Set([ + "hash", + "recordCount", + "storedRecordCount", + "stdoutBytes", + "overflow" +]); +var PHYSICAL_SELECTION_KEYS = /* @__PURE__ */ new Set(["schema", "status", "before", "after"]); +var PHYSICAL_SELECTION_STREAM_KEYS = /* @__PURE__ */ new Set([ + "hash", + "visitedNodeCount", + "recordCount", + "storedRecordCount", + "encodedBytes", + "overflow", + "unstable" +]); +var ENTRY_KEYS = /* @__PURE__ */ new Set(["path", "hash", "size", "type"]); +var OMISSION_KEYS = /* @__PURE__ */ new Set(["path", "size", "type", "reason"]); +var OMISSION_OVERFLOW_KEYS = /* @__PURE__ */ new Set(["ref", "hash", "count"]); +var SNAPSHOT_ORIGINS = /* @__PURE__ */ new Set([ + "change-created", + "legacy-migration", + "explicit" +]); +var OMISSION_TYPES = /* @__PURE__ */ new Set(["file", "directory", "other"]); +var OMISSION_REASONS = /* @__PURE__ */ new Set([ + "file-size", + "file-count", + "total-size", + "manifest-size", + "changed-during-read", + "unreadable", + "gitlink-unavailable", + "gitlink-dirty", + "gitlink-changed", + "legacy-gitlink-boundary", + "git-enumeration-limit", + "git-selection-changed", + "physical-enumeration-limit", + "physical-selection-changed" +]); +var HASH_PATTERN = /^[a-f0-9]{64}$/u; +var GIT_OBJECT_ID_PATTERN = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/u; +var UNREADABLE_ERROR_CODES = /* @__PURE__ */ new Set(["EACCES", "EPERM"]); +var GIT_LIST_STDERR_LIMIT = 64 * 1024; +var GIT_TEXT_STDOUT_LIMIT = 64 * 1024; +var DEFAULT_NATIVE_SNAPSHOT_EXECUTION_BUDGET_MS = 6e4; +var WINDOWS_TASKKILL_ATTEMPT_MS = 1e3; +var WINDOWS_TASKKILL_ATTEMPTS = 2; +var DEFAULT_NATIVE_GIT_SELECTION_LIMITS = { + maxRecords: 2e4, + maxBytes: 8 * 1024 * 1024, + maxRecordBytes: 64 * 1024 +}; +var DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS = { + maxNodes: 2e4, + maxBytes: 8 * 1024 * 1024, + maxPathBytes: 64 * 1024 +}; +function createNativeSnapshotExecution(options) { + const deadlineMs = options.deadlineMs ?? DEFAULT_NATIVE_SNAPSHOT_EXECUTION_BUDGET_MS; + if (!Number.isSafeInteger(deadlineMs) || deadlineMs < 1) { + throw new Error("Native snapshot execution budget must be a positive integer"); + } + return { + deadlineAt: Date.now() + deadlineMs, + gitProcess: options.gitProcess ?? { command: "git" } + }; +} +function remainingNativeSnapshotTime(execution) { + return Math.max(0, execution.deadlineAt - Date.now()); +} +function nativeSnapshotExecutionHasBudget(execution) { + return remainingNativeSnapshotTime(execution) >= 1; +} +function nativeGitSnapshotTimeoutError(cause) { + const error = cause === void 0 ? new Error("Native snapshot deadline exceeded while waiting for Git") : new Error("Native snapshot deadline exceeded while waiting for Git", { cause }); + return Object.assign(error, { code: "GIT_SNAPSHOT_TIMEOUT" }); +} +function isNativeGitSnapshotTimeout(error) { + return error.code === "GIT_SNAPSHOT_TIMEOUT"; +} +function resolveNativePhysicalSelectionLimits(values) { + const limits = { ...DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS, ...values }; + if (!Number.isSafeInteger(limits.maxNodes) || limits.maxNodes < 1 || !Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 1 || !Number.isSafeInteger(limits.maxPathBytes) || limits.maxPathBytes < 1 || limits.maxPathBytes > limits.maxBytes) { + throw new Error("Native physical selection limits must be positive bounded integers"); + } + return limits; +} +async function terminateNativeProcessTree(child, adapter) { + if (adapter.terminateTree) { + await adapter.terminateTree(child); + return; + } + const pid = child.pid; + if (pid === void 0) { + child.kill("SIGKILL"); + return; + } + if (process.platform === "win32") { + const configuredSystemRoot = process.env.SystemRoot ?? process.env.WINDIR; + const systemRoot = configuredSystemRoot && path10.win32.isAbsolute(configuredSystemRoot) ? path10.win32.resolve(configuredSystemRoot) : "C:\\Windows"; + const taskkill = path10.win32.join(systemRoot, "System32", "taskkill.exe"); + if (!path10.win32.isAbsolute(taskkill)) { + child.kill("SIGKILL"); + child.stdin?.destroy(); + child.stdout?.destroy(); + child.stderr?.destroy(); + throw Object.assign(new Error("Native Git taskkill path is not trusted"), { + code: "GIT_SNAPSHOT_TERMINATION_UNCONFIRMED" + }); + } + const runTaskkillAttempt = () => new Promise((resolve) => { + let finished = false; + let killer = null; + let fallbackTimer = null; + const finish = (confirmed, terminateKiller = false) => { + if (finished) return; + finished = true; + if (fallbackTimer) clearTimeout(fallbackTimer); + if (terminateKiller) killer?.kill("SIGKILL"); + resolve(confirmed); + }; + try { + killer = spawn(taskkill, ["/pid", String(pid), "/t", "/f"], { + stdio: "ignore", + windowsHide: true + }); + killer.once("error", () => finish(false)); + killer.once("close", (code) => finish(code === 0)); + fallbackTimer = setTimeout(() => finish(false, true), WINDOWS_TASKKILL_ATTEMPT_MS); + fallbackTimer.unref(); + } catch { + finish(false); + } + }); + for (let attempt = 0; attempt < WINDOWS_TASKKILL_ATTEMPTS; attempt += 1) { + if (await runTaskkillAttempt()) { + child.kill("SIGKILL"); + return; + } + } + child.kill("SIGKILL"); + child.stdin?.destroy(); + child.stdout?.destroy(); + child.stderr?.destroy(); + throw Object.assign( + new Error("Native Git process-tree termination could not be confirmed on Windows"), + { code: "GIT_SNAPSHOT_TERMINATION_UNCONFIRMED" } + ); + } + try { + process.kill(-pid, "SIGKILL"); + } catch (error) { + if (error.code !== "ESRCH") throw error; + } + child.kill("SIGKILL"); +} +function startNativeGitProcess(execution, projectRoot, args, input) { + const remaining = remainingNativeSnapshotTime(execution); + if (remaining < 1) throw nativeGitSnapshotTimeoutError(); + const adapter = execution.gitProcess; + const child = spawn( + adapter.command, + [...adapter.argsPrefix ?? [], "-C", projectRoot, ...args], + { + stdio: [input ? "pipe" : "ignore", "pipe", "pipe"], + windowsHide: true, + detached: process.platform !== "win32" + } + ); + let spawnError = null; + let timedOut = false; + let termination = null; + child.once("error", (error) => { + spawnError = error; + }); + const close = new Promise((resolve) => { + child.once("close", resolve); + }); + const timer = setTimeout(() => { + timedOut = true; + termination = terminateNativeProcessTree(child, adapter).then( + () => ({ error: null }), + (error) => ({ error }) + ); + }, remaining); + timer.unref(); + const completion = close.then(async (code) => { + clearTimeout(timer); + if (timedOut) { + const result2 = await termination; + if (result2?.error) throw nativeGitSnapshotTimeoutError(result2.error); + throw nativeGitSnapshotTimeoutError(); + } + return { code, spawnError }; + }); + return { child, completion }; +} +function resolveNativeGitSelectionLimits(values) { + const limits = { ...DEFAULT_NATIVE_GIT_SELECTION_LIMITS, ...values }; + if (!Number.isSafeInteger(limits.maxRecords) || limits.maxRecords < 1 || !Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 1 || !Number.isSafeInteger(limits.maxRecordBytes) || limits.maxRecordBytes < 1 || limits.maxRecordBytes > limits.maxBytes) { + throw new Error("Native Git selection limits must be positive bounded integers"); + } + return limits; +} +async function runGitBoundedOutput(execution, projectRoot, args, maxBytes = GIT_TEXT_STDOUT_LIMIT) { + const { child, completion } = startNativeGitProcess(execution, projectRoot, args, false); + const stdout = []; + const stderr = []; + let stderrBytes = 0; + let stdoutBytes = 0; + let stdoutOverflow = false; + child.stdout.on("data", (chunk) => { + const remaining = Math.max(0, maxBytes - stdoutBytes); + if (remaining > 0) stdout.push(Buffer.from(chunk).subarray(0, remaining)); + stdoutBytes += chunk.byteLength; + if (stdoutBytes > maxBytes) stdoutOverflow = true; + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + const { code, spawnError } = await completion; + if (code === 0 && spawnError === null && !stdoutOverflow) return Buffer.concat(stdout); + throw Object.assign( + new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ), + { code: "GIT_SNAPSHOT_UNAVAILABLE" } + ); +} +function gitSelectionStreamEvidence(result2) { + return { + hash: result2.digest, + recordCount: result2.recordCount, + storedRecordCount: result2.records.length, + stdoutBytes: result2.stdoutBytes, + overflow: result2.overflow + }; +} +function runGitNullRecords(execution, projectRoot, args, options) { + if (options.outputChunkBytes !== void 0 && (!Number.isSafeInteger(options.outputChunkBytes) || options.outputChunkBytes < 1)) { + throw new Error("Native Git output chunk size must be a positive integer"); + } + return new Promise((resolve, reject) => { + const { child, completion } = startNativeGitProcess( + execution, + projectRoot, + args, + options.stdin !== void 0 + ); + const records = []; + const digest = createHash5("sha256"); + const stderr = []; + let stderrBytes = 0; + let stdoutBytes = 0; + let storedBytes = 0; + let recordCount = 0; + let pending = Buffer.alloc(0); + let droppingRecord = false; + let overflow = false; + let malformed = false; + const consumeChunk = (chunk) => { + let offset = 0; + while (offset < chunk.byteLength) { + const separator = chunk.indexOf(0, offset); + const end = separator < 0 ? chunk.byteLength : separator; + const part = chunk.subarray(offset, end); + if (!droppingRecord && part.byteLength > 0) { + if (pending.byteLength + part.byteLength > options.maxRecordBytes) { + pending = Buffer.alloc(0); + droppingRecord = true; + overflow = true; + } else { + pending = Buffer.concat([pending, part]); + } + } + if (separator < 0) break; + recordCount += 1; + if (pending.byteLength === 0 && !droppingRecord) malformed = true; + const recordBytes = pending.byteLength + 1; + if (!droppingRecord && recordCount <= options.maxRecords && storedBytes + recordBytes <= options.maxBytes) { + records.push(pending); + storedBytes += recordBytes; + } else { + overflow = true; + } + pending = Buffer.alloc(0); + droppingRecord = false; + offset = separator + 1; + } + }; + child.stdout.on("data", (chunk) => { + digest.update(chunk); + stdoutBytes += chunk.byteLength; + if (stdoutBytes > options.maxBytes) overflow = true; + const outputChunkBytes = options.outputChunkBytes; + if (outputChunkBytes === void 0) { + consumeChunk(chunk); + return; + } + for (let offset = 0; offset < chunk.byteLength; offset += outputChunkBytes) { + consumeChunk(chunk.subarray(offset, Math.min(chunk.byteLength, offset + outputChunkBytes))); + } + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + child.stdin?.on("error", () => { + }); + void completion.then(({ code, spawnError }) => { + const acceptedExitCodes = options.acceptedExitCodes ?? [0]; + if (code !== null && acceptedExitCodes.includes(code) && spawnError === null && !malformed && pending.byteLength === 0 && !droppingRecord) { + resolve({ + records, + digest: digest.digest("hex"), + overflow, + recordCount, + stdoutBytes + }); + return; + } + reject( + new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : malformed || pending.byteLength > 0 || droppingRecord ? ": malformed NUL-delimited output" : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ) + ); + }, reject); + if (options.stdin) child.stdin?.end(options.stdin); + }); +} +function decodeGitRecord(value) { + const decoded = value.toString("utf8"); + if (!Buffer.from(decoded, "utf8").equals(value)) { + throw new Error("Native Git snapshot provider returned non-UTF-8 path data"); + } + return decoded; +} +async function runGitCheckIgnore(execution, projectRoot, paths) { + if (paths.length === 0) return /* @__PURE__ */ new Set(); + const result2 = await runGitNullRecords( + execution, + projectRoot, + ["check-ignore", "--no-index", "-z", "--stdin"], + { + ...DEFAULT_NATIVE_GIT_SELECTION_LIMITS, + maxRecords: Math.max(1, paths.length), + acceptedExitCodes: [0, 1], + stdin: Buffer.from(`${paths.join("\0")}\0`, "utf8") + } + ); + if (result2.overflow) { + throw new Error("Native Git check-ignore output exceeded its safety budget"); + } + return new Set( + result2.records.map((record8) => { + const value = decodeGitRecord(record8); + return value.endsWith("/") ? value.slice(0, -1) : value; + }) + ); +} +async function runGitHasOutput(execution, projectRoot, args) { + const { child, completion } = startNativeGitProcess(execution, projectRoot, args, false); + const stderr = []; + let stderrBytes = 0; + let hasOutput = false; + child.stdout.on("data", (chunk) => { + if (chunk.byteLength > 0) hasOutput = true; + }); + child.stderr.on("data", (chunk) => { + if (stderrBytes >= GIT_LIST_STDERR_LIMIT) return; + const remaining = GIT_LIST_STDERR_LIMIT - stderrBytes; + const bounded = Buffer.from(chunk).subarray(0, remaining); + stderr.push(bounded); + stderrBytes += bounded.byteLength; + }); + const { code, spawnError } = await completion; + if (code === 0 && spawnError === null) return hasOutput; + throw new Error( + `git ${args.join(" ")} failed${spawnError ? `: ${spawnError.message}` : stderr.length > 0 ? `: ${Buffer.concat(stderr).toString("utf8").trim()}` : ""}` + ); +} +function safeGitProjectPath(value) { + const withoutDirectoryMarker = value.endsWith("/") ? value.slice(0, -1) : value; + if (withoutDirectoryMarker.length === 0 || withoutDirectoryMarker.includes("\\") || path10.posix.isAbsolute(withoutDirectoryMarker) || /^[A-Za-z]:/u.test(withoutDirectoryMarker) || path10.posix.normalize(withoutDirectoryMarker) !== withoutDirectoryMarker || withoutDirectoryMarker === ".." || withoutDirectoryMarker.startsWith("../") || withoutDirectoryMarker.includes("\0")) { + return null; + } + return withoutDirectoryMarker; +} +function requireSafeGitProjectPaths(values, source) { + return values.map((value) => { + const relative = safeGitProjectPath(value); + if (relative === null) { + throw new Error(`Native Git snapshot provider returned an unsafe ${source} path`); + } + return relative; + }); +} +async function hasGitMetadataBoundary(projectRoot) { + let cursor = path10.resolve(projectRoot); + while (true) { + try { + await fs10.lstat(path10.join(cursor, ".git")); + return true; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const parent = path10.dirname(cursor); + if (parent === cursor) return false; + cursor = parent; + } +} +async function readNativeGitSelectionResults(execution, projectRoot, limits, hooks = {}) { + const options = { + ...limits, + ...hooks.outputChunkBytes === void 0 ? {} : { outputChunkBytes: hooks.outputChunkBytes } + }; + const stagedBefore = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--stage", "-z"], + options + ); + await hooks.afterStageBefore?.(); + const combined = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], + options + ); + await hooks.afterCombined?.(); + const stagedAfter = await runGitNullRecords( + execution, + projectRoot, + ["ls-files", "--stage", "-z"], + options + ); + return { stagedBefore, combined, stagedAfter }; +} +function gitSelectionFence(results) { + return { + stageBefore: gitSelectionStreamEvidence(results.stagedBefore), + combined: gitSelectionStreamEvidence(results.combined), + stageAfter: gitSelectionStreamEvidence(results.stagedAfter) + }; +} +async function nativeGitSnapshotSelection(execution, projectRoot, limits = DEFAULT_NATIVE_GIT_SELECTION_LIMITS, hooks = {}) { + if (!await hasGitMetadataBoundary(projectRoot)) return null; + let insideWorktree; + try { + insideWorktree = await runGitBoundedOutput(execution, projectRoot, [ + "rev-parse", + "--is-inside-work-tree" + ]); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider could not inspect the repository boundary", { + cause: error + }); + } + if (insideWorktree.toString("utf8").trim() !== "true") { + throw new Error("Native Git snapshot provider found .git metadata outside a working tree"); + } + let results; + try { + results = await readNativeGitSelectionResults(execution, projectRoot, limits, hooks); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider failed after repository detection", { + cause: error + }); + } + const { stagedBefore, combined, stagedAfter } = results; + const tracked = /* @__PURE__ */ new Set(); + const gitlinks = /* @__PURE__ */ new Set(); + const addStagedRecords = (records) => { + for (const encoded of records) { + const record8 = decodeGitRecord(encoded); + const separator = record8.indexOf(" "); + if (separator < 0) { + throw new Error("Native Git snapshot provider returned a malformed staged record"); + } + const header = /^(?[0-7]{6}) (?[a-f0-9]{40}|[a-f0-9]{64}) (?[0-3])$/u.exec( + record8.slice(0, separator) + )?.groups; + if (!header) { + throw new Error("Native Git snapshot provider returned a malformed staged header"); + } + const relative = safeGitProjectPath(record8.slice(separator + 1)); + if (relative === null) { + throw new Error("Native Git snapshot provider returned an unsafe staged path"); + } + tracked.add(relative); + if (header.mode === "160000" && header.stage === "0") gitlinks.add(relative); + } + }; + addStagedRecords(stagedBefore.records); + addStagedRecords(stagedAfter.records); + const combinedRecords = combined.records.map(decodeGitRecord); + const combinedPaths = requireSafeGitProjectPaths(combinedRecords, "combined"); + const untracked = new Set(combinedPaths.filter((relative) => !tracked.has(relative))); + const nestedRepositories = new Set( + combinedPaths.filter( + (relative, index) => !tracked.has(relative) && combinedRecords[index].endsWith("/") + ) + ); + for (const gitlink of gitlinks) nestedRepositories.delete(gitlink); + return { + tracked, + untracked, + gitlinks, + nestedRepositories, + omissions: [], + overflow: null, + evidence: null, + initialFence: gitSelectionFence(results) + }; +} +function sameGitSelectionStream(left, right) { + return left.hash === right.hash && left.recordCount === right.recordCount && left.storedRecordCount === right.storedRecordCount && left.stdoutBytes === right.stdoutBytes && left.overflow === right.overflow; +} +function gitSelectionFenceOverflow(fence) { + return fence.stageBefore.overflow || fence.combined.overflow || fence.stageAfter.overflow; +} +function gitSelectionChanged(initial, final) { + return !sameGitSelectionStream(initial.stageBefore, initial.stageAfter) || !sameGitSelectionStream(initial.stageAfter, final.stageBefore) || !sameGitSelectionStream(initial.combined, final.combined) || !sameGitSelectionStream(final.stageBefore, final.stageAfter); +} +async function finalizeNativeGitSnapshotSelection(execution, projectRoot, limits, selection, outputChunkBytes) { + let finalResults; + try { + finalResults = await readNativeGitSelectionResults(execution, projectRoot, limits, { + ...outputChunkBytes === void 0 ? {} : { outputChunkBytes } + }); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + throw new Error("Native Git snapshot provider failed during its final selection fence", { + cause: error + }); + } + const initial = selection.initialFence; + const final = gitSelectionFence(finalResults); + const hasOverflow = gitSelectionFenceOverflow(initial) || gitSelectionFenceOverflow(final); + const changed = gitSelectionChanged(initial, final); + if (hasOverflow) { + selection.omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "git-enumeration-limit" + }); + } + if (changed) { + selection.omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "git-selection-changed" + }); + } + selection.evidence = hasOverflow || changed ? { + schema: "comet.native.git-selection.v1", + status: hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed", + ...initial, + finalStageBefore: final.stageBefore, + finalCombined: final.combined, + finalStageAfter: final.stageAfter + } : null; + if (initial.combined.overflow || final.combined.overflow) { + selection.overflow = { + count: Math.max( + 1, + initial.combined.recordCount - initial.combined.storedRecordCount, + final.combined.recordCount - final.combined.storedRecordCount + ), + hash: sha256Text( + `comet.native.git-selection-overflow.v2 +${JSON.stringify({ + initial: initial.combined, + final: final.combined + })}` + ) + }; + } +} +var PHYSICAL_SELECTION_SUM_MASK = (1n << 256n) - 1n; +function physicalSelectionRecordType(stat) { + if (stat.isFile()) return "file"; + if (stat.isDirectory()) return "directory"; + if (stat.isSymbolicLink()) return "symlink"; + return "other"; +} +async function nativePhysicalSnapshotSelection(options) { + const projectRoot = path10.resolve(options.paths.projectRoot); + const nativeRoot = path10.resolve(options.paths.nativeRoot); + const configFile = path10.resolve(options.paths.configFile); + const selectionFile = path10.join(projectRoot, ".comet", "current-change.json"); + const records = []; + const omissions = []; + const xor = Buffer.alloc(32); + let sum = 0n; + let visitedNodeCount = 0; + let recordCount = 0; + let encodedBytes = 0; + let overflow = false; + let unstable = false; + let stopped = false; + const hasExecutionBudget = () => { + if (remainingNativeSnapshotTime(options.execution) >= 1) return true; + overflow = true; + stopped = true; + return false; + }; + const addRecord = (record8) => { + const encoded = Buffer.from(`${record8.type}\0${record8.path}`, "utf8"); + const digest = createHash5("sha256").update("comet.native.physical-selection-record.v1\0").update(encoded).digest(); + for (let index = 0; index < xor.length; index += 1) xor[index] ^= digest[index]; + sum = sum + BigInt(`0x${digest.toString("hex")}`) & PHYSICAL_SELECTION_SUM_MASK; + recordCount += 1; + encodedBytes += encoded.byteLength; + if (Buffer.byteLength(record8.path, "utf8") > options.limits.maxPathBytes || encodedBytes > options.limits.maxBytes) { + overflow = true; + stopped = true; + return; + } + records.push(record8); + }; + const visit = async (directory) => { + if (stopped) return; + if (!hasExecutionBudget()) return; + let handle; + try { + handle = await fs10.opendir(directory); + } catch (error) { + if (!hasExecutionBudget()) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + if (directory === projectRoot && isUnreadableError(error)) throw error; + unstable ||= isChangedDuringReadError(error); + overflow ||= isUnreadableError(error); + stopped = true; + return; + } + if (!hasExecutionBudget()) { + try { + await handle.close(); + } catch (error) { + if (error.code !== "ERR_DIR_CLOSED") throw error; + } + return; + } + let traversalFailed = false; + let traversalError; + try { + while (!stopped) { + if (!hasExecutionBudget()) break; + let child; + try { + child = await handle.read(); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + if (directory === projectRoot && isUnreadableError(error)) throw error; + unstable ||= isChangedDuringReadError(error); + overflow ||= isUnreadableError(error); + stopped = true; + break; + } + if (!hasExecutionBudget() || child === null) break; + visitedNodeCount += 1; + if (visitedNodeCount > options.limits.maxNodes) { + overflow = true; + stopped = true; + break; + } + const target = path10.join(directory, child.name); + const relative = portableRelative(projectRoot, target); + if (target === configFile || target === selectionFile || sameOrInside(nativeRoot, target) || options.denylist.some((denied) => sameOrInside(denied, target)) || nativeSensitiveRelativePathReason(relative) !== null) { + continue; + } + if (!hasExecutionBudget()) break; + let stat; + try { + stat = await fs10.lstat(target); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + unstable = true; + addRecord({ path: relative, type: "other" }); + omissions.push({ + path: relative, + size: null, + type: "file", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + await options.hooks?.afterNode?.(relative); + if (stopped) break; + if (!hasExecutionBudget()) break; + continue; + } + if (!hasExecutionBudget()) break; + const type = physicalSelectionRecordType(stat); + if (type === "directory") { + if (!hasExecutionBudget()) break; + let realDirectory; + try { + realDirectory = await fs10.realpath(target); + } catch (error) { + if (!hasExecutionBudget()) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + unstable = true; + addRecord({ path: relative, type }); + omissions.push({ + path: relative, + size: null, + type: "directory", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + await options.hooks?.afterNode?.(relative); + if (stopped) break; + if (!hasExecutionBudget()) break; + continue; + } + if (!hasExecutionBudget()) break; + if (!isInsidePath(options.physicalProjectRoot, realDirectory) || sameOrInside(options.physicalNativeRoot, realDirectory)) { + continue; + } + addRecord({ path: relative, type }); + await options.hooks?.afterNode?.(relative); + if (!hasExecutionBudget()) break; + await visit(target); + if (stopped) break; + continue; + } + addRecord({ path: relative, type }); + await options.hooks?.afterNode?.(relative); + if (!hasExecutionBudget()) break; + if (stopped) break; + } + } catch (error) { + traversalFailed = true; + traversalError = error; + } + let closeError; + try { + await handle.close(); + } catch (error) { + closeError = error; + } + const withinBudget = hasExecutionBudget(); + if (traversalFailed) throw traversalError; + if (withinBudget && closeError !== void 0 && closeError.code !== "ERR_DIR_CLOSED") { + throw closeError; + } + }; + await visit(projectRoot); + hasExecutionBudget(); + if (overflow) { + records.length = 0; + recordCount = 0; + encodedBytes = 0; + } else { + records.sort((left, right) => left.path.localeCompare(right.path, "en")); + omissions.sort((left, right) => left.path.localeCompare(right.path, "en")); + } + const hash6 = overflow ? sha256Text( + `comet.native.physical-selection-incomplete.v1 +${JSON.stringify({ + maxNodes: options.limits.maxNodes, + maxBytes: options.limits.maxBytes, + maxPathBytes: options.limits.maxPathBytes + })}` + ) : sha256Text( + `comet.native.physical-selection.v1 +${JSON.stringify({ + visitedNodeCount, + recordCount, + encodedBytes, + xor: xor.toString("hex"), + sum: sum.toString(16).padStart(64, "0") + })}` + ); + return { + records, + omissions, + evidence: { + hash: hash6, + visitedNodeCount, + recordCount, + storedRecordCount: records.length, + encodedBytes, + overflow, + unstable + } + }; +} +function samePhysicalSelectionStream(left, right) { + return left.hash === right.hash && left.visitedNodeCount === right.visitedNodeCount && left.recordCount === right.recordCount && left.storedRecordCount === right.storedRecordCount && left.encodedBytes === right.encodedBytes && left.overflow === right.overflow && left.unstable === right.unstable; +} +function finalizeNativePhysicalSelection(before, after) { + const hasOverflow = before.overflow || after.overflow; + const changed = before.unstable || after.unstable || !samePhysicalSelectionStream(before, after); + const omissions = []; + if (hasOverflow) { + omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "physical-enumeration-limit" + }); + } + if (changed) { + omissions.push({ + path: ".", + size: null, + type: "directory", + reason: "physical-selection-changed" + }); + } + return { + omissions, + evidence: hasOverflow || changed ? { + schema: "comet.native.physical-selection.v1", + status: hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed", + before, + after + } : null + }; +} +function selectionPaths(selection) { + return [.../* @__PURE__ */ new Set([...selection.tracked, ...selection.untracked])].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +async function readGitlinkWorkingTreeHeadHash(execution, target) { + const output = await runGitBoundedOutput(execution, target, ["rev-parse", "--verify", "HEAD"]); + const objectId = output.toString("utf8").trim().toLowerCase(); + if (!GIT_OBJECT_ID_PATTERN.test(objectId)) { + throw new Error("Native Git snapshot provider received an invalid submodule HEAD"); + } + return sha256Text(`gitlink:${objectId}`); +} +async function inspectGitlinkWorkingTree(execution, target) { + const before = await readGitlinkWorkingTreeHeadHash(execution, target); + const dirty = await runGitHasOutput(execution, target, [ + "status", + "--porcelain=v1", + "-z", + "--untracked-files=normal" + ]); + const after = await readGitlinkWorkingTreeHeadHash(execution, target); + if (after !== before) { + throw new Error("Native Git snapshot provider observed a changing submodule HEAD"); + } + return { hash: after, dirty }; +} +function isSnapshotProjectRef(paths, relative) { + if (nativeSensitiveRelativePathReason(relative) !== null) return false; + const target = path10.resolve(paths.projectRoot, ...relative.split("/")); + return !sameOrInside(path10.resolve(paths.nativeRoot), target); +} +function inspectNativeContentSnapshotHealth(value, options = {}) { + const manifest = parseNativeContentSnapshotManifest(value); + const maxRecordedPaths = options.maxRecordedPaths ?? 20; + if (!Number.isSafeInteger(maxRecordedPaths) || maxRecordedPaths < 0) { + throw new Error("Native snapshot health maxRecordedPaths must be a non-negative integer"); + } + const samplePaths = manifest.omitted.slice(0, maxRecordedPaths).map((omission) => omission.path); + return { + complete: manifest.complete, + omittedCount: manifest.omittedCount, + recordedOmissionCount: manifest.omitted.length, + overflowCount: manifest.omissionOverflow?.count ?? 0, + samplePaths, + sampleTruncated: samplePaths.length < manifest.omittedCount + }; +} +function portableRelative(root, target) { + return path10.relative(root, target).split(path10.sep).join("/"); +} +function normalizedDenylist(projectRoot, values) { + return values.map((value) => path10.resolve(projectRoot, ...value.split(/[\\/]/u))); +} +function sameOrInside(root, target) { + const normalizedRoot = path10.resolve(root); + const normalizedTarget = path10.resolve(target); + return normalizedTarget === normalizedRoot || isInsidePath(normalizedRoot, normalizedTarget); +} +function isUnreadableError(error) { + return UNREADABLE_ERROR_CODES.has(error.code ?? ""); +} +function isChangedDuringReadError(error) { + return error.code === "ENOENT"; +} +function serializedManifestBytes(manifest) { + return Buffer.byteLength(JSON.stringify(manifest, null, 2) + "\n"); +} +function snapshotPolicyHash(include, exclude) { + return sha256Text( + `comet.native.snapshot-policy.v1 +${JSON.stringify({ include, exclude, hash: "sha256" })}` + ); +} +function epsilonClosure(tokens, positions, checkpoint) { + const closure = new Set(positions); + const pending = [...positions]; + while (pending.length > 0) { + if (checkpoint && !checkpoint()) return null; + const position = pending.pop(); + const token = tokens[position]; + if (token && (token.kind === "star" || token.kind === "globstar" || token.kind === "globstar-slash") && !closure.has(position + 1)) { + closure.add(position + 1); + pending.push(position + 1); + } + } + return closure; +} +function cooperativePatternCheckpoint(hasBudget) { + let operationsUntilCheck = 0; + return () => { + if (operationsUntilCheck > 0) { + operationsUntilCheck -= 1; + return true; + } + if (!hasBudget()) return false; + operationsUntilCheck = 63; + return true; + }; +} +function compileNativeSnapshotPattern(pattern) { + const normalized = normalizeNativeSnapshotPattern(pattern, "Native snapshot pattern"); + const tokens = []; + for (let index = 0; index < normalized.length; index += 1) { + const character = normalized[index]; + if (character === "*" && normalized[index + 1] === "*") { + index += 1; + if (normalized[index + 1] === "/") { + index += 1; + tokens.push({ kind: "globstar-slash" }); + } else { + tokens.push({ kind: "globstar" }); + } + } else if (character === "*") { + tokens.push({ kind: "star" }); + } else if (character === "?") { + tokens.push({ kind: "question" }); + } else { + tokens.push({ kind: "literal", value: character }); + } + } + return (relative, hasBudget) => { + const checkpoint = hasBudget ? cooperativePatternCheckpoint(hasBudget) : void 0; + if (checkpoint && !checkpoint()) return false; + let positions = epsilonClosure(tokens, /* @__PURE__ */ new Set([0]), checkpoint); + if (positions === null) return false; + for (const character of relative) { + if (checkpoint && !checkpoint()) return false; + const next = /* @__PURE__ */ new Set(); + for (const position of positions) { + if (checkpoint && !checkpoint()) return false; + const token = tokens[position]; + if (!token) continue; + if (token.kind === "literal" && token.value === character) { + next.add(position + 1); + } else if (token.kind === "question" && character !== "/") { + next.add(position + 1); + } else if (token.kind === "star" && character !== "/") { + next.add(position); + } else if (token.kind === "globstar") { + next.add(position); + } else if (token.kind === "globstar-slash") { + next.add(position); + if (character === "/") next.add(position + 1); + } + } + positions = epsilonClosure(tokens, next, checkpoint); + if (positions === null) return false; + if (positions.size === 0) return false; + } + return epsilonClosure(tokens, positions, checkpoint)?.has(tokens.length) ?? false; + }; +} +function resolveSnapshotPolicy(value) { + if (value === void 0) return void 0; + const include = [ + ...new Set(value.include.map((item) => normalizeNativeSnapshotPattern(item, "include"))) + ].sort((left, right) => left.localeCompare(right, "en")); + const exclude = [ + ...new Set(value.exclude.map((item) => normalizeNativeSnapshotPattern(item, "exclude"))) + ].sort((left, right) => left.localeCompare(right, "en")); + if (include.length === 0) throw new Error("Native snapshot policy include must not be empty"); + const hash6 = snapshotPolicyHash(include, exclude); + if ("hash" in value && value.hash !== hash6) { + throw new Error("Native snapshot policy hash is invalid"); + } + return { + manifest: { + schema: "comet.native.snapshot-policy.v1", + include, + exclude, + hash: hash6 + }, + includeMatchers: include.map(compileNativeSnapshotPattern), + excludeMatchers: exclude.map(compileNativeSnapshotPattern) + }; +} +function snapshotPolicyIncludes(policy, relative, execution) { + if (!policy) return true; + const hasBudget = () => nativeSnapshotExecutionHasBudget(execution); + let included = false; + for (const matcher of policy.includeMatchers) { + if (!hasBudget()) return false; + if (matcher(relative, hasBudget)) { + included = true; + break; + } + } + if (!included) return false; + for (const matcher of policy.excludeMatchers) { + if (!hasBudget()) return false; + if (matcher(relative, hasBudget)) return false; + } + return true; +} +function foldSnapshotOverflowHash(previous, kind, value) { + const payload = JSON.stringify(value); + return sha256Text( + `${previous} +${Buffer.byteLength(kind)}:${kind} +${Buffer.byteLength(payload)}:${payload}` + ); +} +function isSelectionIntegrityOmission(omission) { + return omission.reason === "git-enumeration-limit" || omission.reason === "git-selection-changed" || omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed"; +} +function takeLastCompactableOmission(omissions) { + for (let index = omissions.length - 1; index >= 0; index -= 1) { + const omission = omissions[index]; + if (isSelectionIntegrityOmission(omission)) continue; + omissions.splice(index, 1); + return omission; + } + return null; +} +function sameFileIdentity4(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function sha256FileBounded(file, maxBytes, expected, execution) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + let handle; + try { + handle = await fs10.open(file, "r"); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + throw error; + } + if (!nativeSnapshotExecutionHasBudget(execution)) { + await handle.close(); + return { status: "budget-exhausted" }; + } + const hash6 = createHash5("sha256"); + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + let bytes = 0; + try { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const opened = await handle.stat(); + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + if (!opened.isFile() || !sameFileIdentity4(expected, opened)) return { status: "changed" }; + while (true) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const remaining = maxBytes + 1 - bytes; + if (remaining < 1) return { status: "changed" }; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + if (result2.bytesRead === 0) { + if (!nativeSnapshotExecutionHasBudget(execution)) return { status: "budget-exhausted" }; + const finalStat = await handle.stat(); + if (!nativeSnapshotExecutionHasBudget(execution)) { + return { status: "budget-exhausted" }; + } + if (!finalStat.isFile() || !sameFileIdentity4(opened, finalStat)) { + return { status: "changed" }; + } + return { status: "complete", hash: hash6.digest("hex"), bytes, finalStat }; + } + if (bytes + result2.bytesRead > maxBytes) return { status: "changed" }; + hash6.update(buffer.subarray(0, result2.bytesRead)); + bytes += result2.bytesRead; + } + } finally { + await handle.close(); + } +} +function record3(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function rejectUnknown3(value, keys, label) { + const unknown = Object.keys(value).find((key) => !keys.has(key)); + if (unknown) throw new Error(`${label} contains unknown field: ${unknown}`); +} +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function nonNegativeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function snapshotPath(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + const normalized = path10.posix.normalize(value); + if (normalized !== value || path10.posix.isAbsolute(value) || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function parseEntry(value, index) { + const entry2 = record3(value, `Native snapshot entry ${index}`); + rejectUnknown3(entry2, ENTRY_KEYS, `Native snapshot entry ${index}`); + const entryPath = snapshotPath(entry2.path, `Native snapshot entry ${index} path`); + if (typeof entry2.hash !== "string" || !HASH_PATTERN.test(entry2.hash)) { + throw new Error(`Native snapshot entry ${index} hash is invalid`); + } + if (entry2.type !== "file") throw new Error(`Native snapshot entry ${index} type is invalid`); + return { + path: entryPath, + hash: entry2.hash, + size: nonNegativeInteger(entry2.size, `Native snapshot entry ${index} size`), + type: "file" + }; +} +function parseOmission(value, index) { + const omission = record3(value, `Native snapshot omission ${index}`); + rejectUnknown3(omission, OMISSION_KEYS, `Native snapshot omission ${index}`); + if (!OMISSION_TYPES.has(omission.type)) { + throw new Error(`Native snapshot omission ${index} type is invalid`); + } + if (!OMISSION_REASONS.has(omission.reason)) { + throw new Error(`Native snapshot omission ${index} reason is invalid`); + } + return { + path: snapshotPath(omission.path, `Native snapshot omission ${index} path`), + size: omission.size === null ? null : nonNegativeInteger(omission.size, `Native snapshot omission ${index} size`), + type: omission.type, + reason: omission.reason + }; +} +function parseOmissionOverflow(value) { + const overflow = record3(value, "Native snapshot omission overflow"); + rejectUnknown3(overflow, OMISSION_OVERFLOW_KEYS, "Native snapshot omission overflow"); + if (typeof overflow.hash !== "string" || !HASH_PATTERN.test(overflow.hash)) { + throw new Error("Native snapshot omission overflow hash is invalid"); + } + const expectedRef = `native-snapshot://omitted-overflow/${overflow.hash}`; + if (overflow.ref !== expectedRef) { + throw new Error("Native snapshot omission overflow ref is invalid"); + } + return { + ref: expectedRef, + hash: overflow.hash, + count: positiveInteger(overflow.count, "Native snapshot omission overflow count") + }; +} +function parseGitSelectionStreamEvidence(value, label) { + const stream = record3(value, label); + rejectUnknown3(stream, GIT_SELECTION_STREAM_KEYS, label); + if (typeof stream.hash !== "string" || !HASH_PATTERN.test(stream.hash)) { + throw new Error(`${label} hash is invalid`); + } + if (typeof stream.overflow !== "boolean") { + throw new Error(`${label} overflow flag is invalid`); + } + const recordCount = nonNegativeInteger(stream.recordCount, `${label} recordCount`); + const storedRecordCount = nonNegativeInteger( + stream.storedRecordCount, + `${label} storedRecordCount` + ); + const stdoutBytes = nonNegativeInteger(stream.stdoutBytes, `${label} stdoutBytes`); + if (storedRecordCount > recordCount || !stream.overflow && storedRecordCount !== recordCount) { + throw new Error(`${label} stored record count is inconsistent`); + } + return { + hash: stream.hash, + recordCount, + storedRecordCount, + stdoutBytes, + overflow: stream.overflow + }; +} +function parseGitSelectionEvidence(value) { + const selection = record3(value, "Native Git selection evidence"); + rejectUnknown3(selection, GIT_SELECTION_KEYS, "Native Git selection evidence"); + if (selection.schema !== "comet.native.git-selection.v1") { + throw new Error("Native Git selection evidence schema is invalid"); + } + if (selection.status !== "overflow" && selection.status !== "changed" && selection.status !== "overflow-and-changed") { + throw new Error("Native Git selection evidence status is invalid"); + } + const stageBefore = parseGitSelectionStreamEvidence( + selection.stageBefore, + "Native Git selection stageBefore" + ); + const combined = parseGitSelectionStreamEvidence( + selection.combined, + "Native Git selection combined" + ); + const stageAfter = parseGitSelectionStreamEvidence( + selection.stageAfter, + "Native Git selection stageAfter" + ); + const finalStageBefore = parseGitSelectionStreamEvidence( + selection.finalStageBefore, + "Native Git selection finalStageBefore" + ); + const finalCombined = parseGitSelectionStreamEvidence( + selection.finalCombined, + "Native Git selection finalCombined" + ); + const finalStageAfter = parseGitSelectionStreamEvidence( + selection.finalStageAfter, + "Native Git selection finalStageAfter" + ); + const hasOverflow = [ + stageBefore, + combined, + stageAfter, + finalStageBefore, + finalCombined, + finalStageAfter + ].some((stream) => stream.overflow); + const changed = !sameGitSelectionStream(stageBefore, stageAfter) || !sameGitSelectionStream(stageAfter, finalStageBefore) || !sameGitSelectionStream(combined, finalCombined) || !sameGitSelectionStream(finalStageBefore, finalStageAfter); + const expectedStatus = hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed"; + if (!hasOverflow && !changed) { + throw new Error("Native Git selection evidence must describe an exceptional selection"); + } + if (selection.status !== expectedStatus) { + throw new Error("Native Git selection evidence status is inconsistent"); + } + return { + schema: "comet.native.git-selection.v1", + status: expectedStatus, + stageBefore, + combined, + stageAfter, + finalStageBefore, + finalCombined, + finalStageAfter + }; +} +function parsePhysicalSelectionStreamEvidence(value, label) { + const stream = record3(value, label); + rejectUnknown3(stream, PHYSICAL_SELECTION_STREAM_KEYS, label); + if (typeof stream.hash !== "string" || !HASH_PATTERN.test(stream.hash)) { + throw new Error(`${label} hash is invalid`); + } + if (typeof stream.overflow !== "boolean" || typeof stream.unstable !== "boolean") { + throw new Error(`${label} flags are invalid`); + } + const visitedNodeCount = nonNegativeInteger(stream.visitedNodeCount, `${label} visitedNodeCount`); + const recordCount = nonNegativeInteger(stream.recordCount, `${label} recordCount`); + const storedRecordCount = nonNegativeInteger( + stream.storedRecordCount, + `${label} storedRecordCount` + ); + const encodedBytes = nonNegativeInteger(stream.encodedBytes, `${label} encodedBytes`); + if (storedRecordCount > recordCount || !stream.overflow && storedRecordCount !== recordCount) { + throw new Error(`${label} stored record count is inconsistent`); + } + return { + hash: stream.hash, + visitedNodeCount, + recordCount, + storedRecordCount, + encodedBytes, + overflow: stream.overflow, + unstable: stream.unstable + }; +} +function parsePhysicalSelectionEvidence(value) { + const selection = record3(value, "Native physical selection evidence"); + rejectUnknown3(selection, PHYSICAL_SELECTION_KEYS, "Native physical selection evidence"); + if (selection.schema !== "comet.native.physical-selection.v1") { + throw new Error("Native physical selection evidence schema is invalid"); + } + if (selection.status !== "overflow" && selection.status !== "changed" && selection.status !== "overflow-and-changed") { + throw new Error("Native physical selection evidence status is invalid"); + } + const before = parsePhysicalSelectionStreamEvidence( + selection.before, + "Native physical selection before" + ); + const after = parsePhysicalSelectionStreamEvidence( + selection.after, + "Native physical selection after" + ); + const hasOverflow = before.overflow || after.overflow; + const changed = before.unstable || after.unstable || !samePhysicalSelectionStream(before, after); + if (!hasOverflow && !changed) { + throw new Error("Native physical selection evidence must describe an exceptional selection"); + } + const expectedStatus = hasOverflow && changed ? "overflow-and-changed" : hasOverflow ? "overflow" : "changed"; + if (selection.status !== expectedStatus) { + throw new Error("Native physical selection evidence status is inconsistent"); + } + return { + schema: "comet.native.physical-selection.v1", + status: expectedStatus, + before, + after + }; +} +function parseNativeContentSnapshotManifest(value) { + const manifest = record3(value, "Native content snapshot manifest"); + rejectUnknown3(manifest, MANIFEST_KEYS, "Native content snapshot manifest"); + if (manifest.schema !== "comet.native.content-snapshot.v1") { + throw new Error("Unsupported Native content snapshot schema"); + } + if (!SNAPSHOT_ORIGINS.has(manifest.origin)) { + throw new Error("Native content snapshot origin is invalid"); + } + let capture; + if (manifest.capture !== void 0) { + const captureValue = record3(manifest.capture, "Native content snapshot capture"); + rejectUnknown3(captureValue, CAPTURE_KEYS, "Native content snapshot capture"); + if (captureValue.provider !== "git" && captureValue.provider !== "physical-tree") { + throw new Error("Native content snapshot capture provider is invalid"); + } + const gitSelection2 = captureValue.gitSelection === void 0 ? void 0 : parseGitSelectionEvidence(captureValue.gitSelection); + const physicalSelection2 = captureValue.physicalSelection === void 0 ? void 0 : parsePhysicalSelectionEvidence(captureValue.physicalSelection); + let projection = null; + if (captureValue.projection !== void 0) { + const projectionValue = record3(captureValue.projection, "Native content snapshot projection"); + rejectUnknown3(projectionValue, GIT_PROJECTION_KEYS, "Native content snapshot projection"); + if (projectionValue.provider !== "git") { + throw new Error("Native content snapshot projection provider is invalid"); + } + projection = { + provider: "git", + ...projectionValue.selection === void 0 ? {} : { selection: parseGitSelectionEvidence(projectionValue.selection) } + }; + } + if (captureValue.provider === "git") { + if (physicalSelection2 || projection) { + throw new Error("Native Git capture cannot include physical or projection evidence"); + } + capture = { + provider: "git", + ...gitSelection2 ? { gitSelection: gitSelection2 } : {} + }; + } else { + if (gitSelection2) { + throw new Error("Native physical-tree capture cannot include direct Git evidence"); + } + if (physicalSelection2 && projection) { + throw new Error("Native physical-tree capture cannot combine selection and projection"); + } + capture = projection ? { provider: "physical-tree", projection } : { + provider: "physical-tree", + ...physicalSelection2 ? { physicalSelection: physicalSelection2 } : {} + }; + } + } + if (typeof manifest.createdAt !== "string" || Number.isNaN(Date.parse(manifest.createdAt))) { + throw new Error("Native content snapshot timestamp is invalid"); + } + if (typeof manifest.complete !== "boolean") { + throw new Error("Native content snapshot complete flag is invalid"); + } + const limitValue = record3(manifest.limits, "Native content snapshot limits"); + rejectUnknown3(limitValue, LIMIT_KEYS, "Native content snapshot limits"); + const limits = { + maxFiles: positiveInteger(limitValue.maxFiles, "Native snapshot maxFiles"), + maxFileBytes: positiveInteger(limitValue.maxFileBytes, "Native snapshot maxFileBytes"), + maxTotalBytes: positiveInteger(limitValue.maxTotalBytes, "Native snapshot maxTotalBytes"), + maxManifestBytes: positiveInteger( + limitValue.maxManifestBytes, + "Native snapshot maxManifestBytes" + ), + ...limitValue.maxDurationMs === void 0 ? {} : { + maxDurationMs: positiveInteger(limitValue.maxDurationMs, "Native snapshot maxDurationMs") + } + }; + let policy; + if (manifest.policy !== void 0) { + const policyValue = record3(manifest.policy, "Native snapshot policy"); + rejectUnknown3(policyValue, POLICY_KEYS, "Native snapshot policy"); + if (policyValue.schema !== "comet.native.snapshot-policy.v1") { + throw new Error("Native snapshot policy schema is invalid"); + } + if (!Array.isArray(policyValue.include) || !Array.isArray(policyValue.exclude)) { + throw new Error("Native snapshot policy patterns must be arrays"); + } + policy = resolveSnapshotPolicy({ + include: policyValue.include, + exclude: policyValue.exclude, + hash: policyValue.hash, + schema: "comet.native.snapshot-policy.v1" + }).manifest; + } + if (!Array.isArray(manifest.entries) || !Array.isArray(manifest.omitted)) { + throw new Error("Native content snapshot entries and omissions must be arrays"); + } + const entries = manifest.entries.map(parseEntry); + const omitted = manifest.omitted.map(parseOmission); + const omittedCount = nonNegativeInteger( + manifest.omittedCount, + "Native content snapshot omittedCount" + ); + const omissionOverflow = manifest.omissionOverflow === void 0 ? void 0 : parseOmissionOverflow(manifest.omissionOverflow); + if (entries.length > limits.maxFiles) { + throw new Error("Native content snapshot exceeds its file-count limit"); + } + if (entries.some((entry2) => entry2.size > limits.maxFileBytes) || entries.reduce((total, entry2) => total + entry2.size, 0) > limits.maxTotalBytes) { + throw new Error("Native content snapshot exceeds its byte limits"); + } + if (new Set(entries.map((entry2) => entry2.path)).size !== entries.length) { + throw new Error("Native content snapshot contains duplicate paths"); + } + if (omitted.length > MAX_RECORDED_OMISSIONS || omittedCount < omitted.length) { + throw new Error("Native content snapshot omission count is invalid"); + } + const overflowCount = omittedCount - omitted.length; + if (overflowCount === 0 && omissionOverflow || overflowCount > 0 && omissionOverflow?.count !== overflowCount) { + throw new Error("Native content snapshot omission overflow is inconsistent"); + } + if (manifest.complete !== (omittedCount === 0)) { + throw new Error("Native content snapshot completeness is inconsistent"); + } + const enumerationOmissions = omitted.filter( + (omission) => omission.reason === "git-enumeration-limit" + ); + const selectionChangedOmissions = omitted.filter( + (omission) => omission.reason === "git-selection-changed" + ); + for (const omission of [...enumerationOmissions, ...selectionChangedOmissions]) { + if (omission.path !== "." || omission.size !== null || omission.type !== "directory") { + throw new Error("Native Git selection omission must use the project-root sentinel"); + } + } + if (enumerationOmissions.length > 1 || selectionChangedOmissions.length > 1) { + throw new Error("Native Git selection omissions must not be duplicated"); + } + const gitSelection = capture?.provider === "git" ? capture.gitSelection : capture?.projection?.selection; + const evidenceHasOverflow = gitSelection?.status === "overflow" || gitSelection?.status === "overflow-and-changed"; + const evidenceHasSelectionChange = gitSelection?.status === "changed" || gitSelection?.status === "overflow-and-changed"; + if (evidenceHasOverflow !== (enumerationOmissions.length === 1)) { + throw new Error("Native Git enumeration omission and selection evidence are inconsistent"); + } + if (evidenceHasSelectionChange !== (selectionChangedOmissions.length === 1)) { + throw new Error("Native Git selection-change omission and selection evidence are inconsistent"); + } + const physicalEnumerationOmissions = omitted.filter( + (omission) => omission.reason === "physical-enumeration-limit" + ); + const physicalChangedOmissions = omitted.filter( + (omission) => omission.reason === "physical-selection-changed" + ); + for (const omission of [...physicalEnumerationOmissions, ...physicalChangedOmissions]) { + if (omission.path !== "." || omission.size !== null || omission.type !== "directory") { + throw new Error("Native physical selection omission must use the project-root sentinel"); + } + } + if (physicalEnumerationOmissions.length > 1 || physicalChangedOmissions.length > 1) { + throw new Error("Native physical selection omissions must not be duplicated"); + } + const physicalSelection = capture?.physicalSelection; + const physicalEvidenceHasOverflow = physicalSelection?.status === "overflow" || physicalSelection?.status === "overflow-and-changed"; + const physicalEvidenceHasChange = physicalSelection?.status === "changed" || physicalSelection?.status === "overflow-and-changed"; + if (physicalEvidenceHasOverflow !== (physicalEnumerationOmissions.length === 1)) { + throw new Error("Native physical enumeration omission and evidence are inconsistent"); + } + if (physicalEvidenceHasChange !== (physicalChangedOmissions.length === 1)) { + throw new Error("Native physical selection-change omission and evidence are inconsistent"); + } + const parsed = { + schema: "comet.native.content-snapshot.v1", + origin: manifest.origin, + ...capture ? { capture } : {}, + createdAt: manifest.createdAt, + complete: manifest.complete, + limits, + ...policy ? { policy } : {}, + entries, + omitted, + omittedCount, + ...omissionOverflow ? { omissionOverflow } : {} + }; + if (serializedManifestBytes(parsed) > limits.maxManifestBytes) { + throw new Error("Native content snapshot exceeds its manifest byte limit"); + } + return parsed; +} +async function filterNativeContentSnapshotToProjectScope(paths, value, options = {}) { + const manifest = parseNativeContentSnapshotManifest(value); + if (manifest.capture?.provider === "git" || manifest.capture?.projection || manifest.capture?.physicalSelection) { + return manifest; + } + const projectRoot = path10.resolve(paths.projectRoot); + const execution = createNativeSnapshotExecution(options); + const gitSelectionLimits = resolveNativeGitSelectionLimits(options.gitSelectionLimits); + const selection = await nativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + options.gitSelectionHooks + ); + if (selection === null) return manifest; + const selected = new Set( + selectionPaths(selection).filter((relative) => isSnapshotProjectRef(paths, relative)) + ); + const gitlinkPaths = [...selection.gitlinks].filter((relative) => selected.has(relative)); + const nestedRepositoryPaths = [...selection.nestedRepositories].filter( + (relative) => isSnapshotProjectRef(paths, relative) + ); + const atOrBeneath = (relative, boundaries) => boundaries.some((boundary) => relative === boundary || relative.startsWith(`${boundary}/`)); + const beneathGitlink = (relative) => atOrBeneath(relative, gitlinkPaths); + const beneathNestedRepository = (relative) => atOrBeneath(relative, nestedRepositoryPaths); + const ignoredCandidates = [ + ...manifest.entries.map((entry2) => entry2.path), + ...manifest.omitted.flatMap( + (omission) => omission.type === "directory" ? [omission.path, `${omission.path}/`] : [omission.path] + ) + ].filter( + (relative) => !selected.has(relative) && !beneathGitlink(relative) && !beneathNestedRepository(relative) + ); + const ignored = await runGitCheckIgnore(execution, projectRoot, ignoredCandidates); + const entries = manifest.entries.filter( + (entry2) => !beneathGitlink(entry2.path) && !beneathNestedRepository(entry2.path) && isSnapshotProjectRef(paths, entry2.path) && (selected.has(entry2.path) || !ignored.has(entry2.path)) + ); + await finalizeNativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + selection, + options.gitSelectionHooks?.outputChunkBytes + ); + const boundaryOmissions = [...selection.omissions]; + for (const gitlink of gitlinkPaths) { + boundaryOmissions.push({ + path: gitlink, + size: null, + type: "directory", + reason: "legacy-gitlink-boundary" + }); + } + entries.sort((left, right) => left.path.localeCompare(right.path, "en")); + const omitted = manifest.omitted.filter((omission) => { + if (beneathGitlink(omission.path)) return false; + if (beneathNestedRepository(omission.path)) return false; + if (!isSnapshotProjectRef(paths, omission.path)) return false; + if (!ignored.has(omission.path)) return true; + return selected.has(omission.path) || [...selected].some((relative) => relative.startsWith(`${omission.path}/`)); + }); + for (const omission of boundaryOmissions) { + if (!omitted.some( + (current) => current.path === omission.path && current.reason === omission.reason + )) { + omitted.push(omission); + } + } + omitted.sort((left, right) => left.path.localeCompare(right.path, "en")); + let overflowCount = manifest.omissionOverflow?.count ?? 0; + let overflowHash = manifest.omissionOverflow?.hash ?? sha256Text("comet.native.snapshot-omission-overflow.v1"); + if (selection.overflow) { + overflowCount += selection.overflow.count; + overflowHash = foldSnapshotOverflowHash(overflowHash, "git-selection", { + source: "git-selection", + ...selection.overflow + }); + } + const foldOverflow = (omission) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "omission", omission); + }; + while (omitted.length > MAX_RECORDED_OMISSIONS) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) { + throw new Error( + "Projected Native snapshot has too many required selection-integrity omissions" + ); + } + foldOverflow(omission); + } + const buildProjection = () => ({ + ...manifest, + capture: { + provider: "physical-tree", + projection: { + provider: "git", + ...selection.evidence ? { selection: selection.evidence } : {} + } + }, + complete: omitted.length + overflowCount === 0, + entries, + omitted, + omittedCount: omitted.length + overflowCount, + ...overflowCount > 0 ? { + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${overflowHash}`, + hash: overflowHash, + count: overflowCount + } + } : {} + }); + let projected = buildProjection(); + while (serializedManifestBytes(projected) > manifest.limits.maxManifestBytes) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) { + throw new Error("Projected Native snapshot cannot fit its manifest byte limit"); + } + foldOverflow(omission); + projected = buildProjection(); + } + return parseNativeContentSnapshotManifest(projected); +} +function nativeBaselineManifestFile(paths, name) { + if (!CHANGE_NAME_PATTERN.test(name)) throw new Error(`Invalid Native change name: ${name}`); + const changeDir = path10.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, changeDir)) throw new Error("Native change path escaped"); + return path10.join(changeDir, "runtime", "baseline-manifest.json"); +} +async function createNativeContentSnapshot(paths, options = {}) { + const limits = { + maxFiles: options.limits?.maxFiles ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxFiles, + maxFileBytes: options.limits?.maxFileBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxFileBytes, + maxTotalBytes: options.limits?.maxTotalBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxTotalBytes, + maxManifestBytes: options.limits?.maxManifestBytes ?? DEFAULT_NATIVE_SNAPSHOT_LIMITS.maxManifestBytes, + ...options.limits?.maxDurationMs === void 0 ? {} : { maxDurationMs: options.limits.maxDurationMs } + }; + const policy = resolveSnapshotPolicy(options.policy); + const execution = createNativeSnapshotExecution({ + ...options, + deadlineMs: options.deadlineMs ?? limits.maxDurationMs + }); + const gitSelectionLimits = resolveNativeGitSelectionLimits(options.gitSelectionLimits); + const physicalSelectionLimits = resolveNativePhysicalSelectionLimits( + options.physicalSelectionLimits + ); + if (limits.maxFiles < 1 || limits.maxFileBytes < 1 || limits.maxTotalBytes < 1 || limits.maxManifestBytes < 1) { + throw new Error("Native snapshot limits must be positive"); + } + const projectRoot = path10.resolve(paths.projectRoot); + const physicalProjectRoot = await fs10.realpath(projectRoot); + const nativeRoot = path10.resolve(paths.nativeRoot); + const physicalNativeRoot = await fs10.realpath(nativeRoot); + const configFile = path10.resolve(paths.configFile); + const selectionFile = path10.join(projectRoot, ".comet", "current-change.json"); + const denylist = normalizedDenylist(projectRoot, options.denylist ?? []); + const entries = []; + const omitted = []; + const capturedEntryValidations = /* @__PURE__ */ new Map(); + const capturedTrackedAbsences = /* @__PURE__ */ new Map(); + let omittedCount = 0; + let overflowCount = 0; + let overflowHash = sha256Text("comet.native.snapshot-omission-overflow.v1"); + let totalBytes = 0; + let notifiedFirstEntry = false; + let physicalSelectionEvidence = null; + const foldOverflow = (value) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "omission", value); + }; + const omit = (value) => { + omittedCount += 1; + if (omitted.length < MAX_RECORDED_OMISSIONS) { + omitted.push(value); + return; + } + if (isSelectionIntegrityOmission(value)) { + const displaced = takeLastCompactableOmission(omitted); + if (displaced === null) { + throw new Error("Native snapshot has too many required selection-integrity omissions"); + } + foldOverflow(displaced); + omitted.push(value); + return; + } + foldOverflow(value); + }; + const foldGitSelectionOverflow = (value) => { + omittedCount += value.count; + overflowCount += value.count; + overflowHash = foldSnapshotOverflowHash(overflowHash, "git-selection", { + source: "git-selection", + ...value + }); + }; + const foldManifestEntryOverflow = (entry2) => { + overflowCount += 1; + overflowHash = foldSnapshotOverflowHash(overflowHash, "manifest-entry", { + reason: "manifest-size", + entry: entry2 + }); + }; + const recordCapturedEntry = async (entry2, validation2) => { + entries.push(entry2); + totalBytes += entry2.size; + capturedEntryValidations.set(entry2.path, validation2); + if (!notifiedFirstEntry) { + notifiedFirstEntry = true; + await options.gitSelectionHooks?.afterFirstEntryCaptured?.(entry2.path); + } + }; + const invalidateCapturedEntry = (relative, omission) => { + const index = entries.findIndex((entry3) => entry3.path === relative); + if (index < 0) return; + const [entry2] = entries.splice(index, 1); + totalBytes -= entry2.size; + capturedEntryValidations.delete(relative); + omit(omission); + }; + const captureFile = async (target, relative, before) => { + if (!before.isFile() || before.isSymbolicLink()) return; + if (!nativeSnapshotExecutionHasBudget(execution)) return; + let realTarget; + try { + realTarget = await fs10.realpath(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: before.size, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isInsidePath(physicalProjectRoot, realTarget) || sameOrInside(physicalNativeRoot, realTarget)) { + return; + } + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size: before.size, type: "file", reason: "file-count" }); + return; + } + if (before.size > limits.maxFileBytes) { + omit({ path: relative, size: before.size, type: "file", reason: "file-size" }); + return; + } + if (totalBytes + before.size > limits.maxTotalBytes) { + omit({ path: relative, size: before.size, type: "file", reason: "total-size" }); + return; + } + let boundedHash; + let after; + let afterRealTarget; + try { + boundedHash = await sha256FileBounded(realTarget, before.size, before, execution); + if (boundedHash.status === "budget-exhausted" || !nativeSnapshotExecutionHasBudget(execution)) { + return; + } + if (boundedHash.status === "changed") { + omit({ path: relative, size: null, type: "file", reason: "changed-during-read" }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + afterRealTarget = await fs10.realpath(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + after = await fs10.lstat(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: before.size, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (boundedHash.bytes !== before.size || afterRealTarget !== realTarget || !sameFileIdentity4(before, boundedHash.finalStat) || !sameFileIdentity4(boundedHash.finalStat, after) || !after.isFile() || after.isSymbolicLink() || after.size !== before.size || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs) { + omit({ + path: relative, + size: after.isFile() ? after.size : null, + type: after.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + await recordCapturedEntry( + { path: relative, hash: boundedHash.hash, size: after.size, type: "file" }, + { kind: "file", target, realTarget, stat: after } + ); + }; + const captureSymbolicLink = async (target, relative, before) => { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + let firstTarget; + let secondTarget; + let after; + try { + firstTarget = await fs10.readlink(target, { encoding: "buffer" }); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + after = await fs10.lstat(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + secondTarget = await fs10.readlink(target, { encoding: "buffer" }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: null, + type: "other", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + return; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!after.isSymbolicLink() || !sameFileIdentity4(before, after) || after.mtimeMs !== before.mtimeMs || after.ctimeMs !== before.ctimeMs || !firstTarget.equals(secondTarget)) { + omit({ path: relative, size: null, type: "other", reason: "changed-during-read" }); + return; + } + const size = firstTarget.byteLength; + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size, type: "other", reason: "file-count" }); + return; + } + if (size > limits.maxFileBytes) { + omit({ path: relative, size, type: "other", reason: "file-size" }); + return; + } + if (totalBytes + size > limits.maxTotalBytes) { + omit({ path: relative, size, type: "other", reason: "total-size" }); + return; + } + const hash6 = createHash5("sha256").update("symlink\0").update(firstTarget).digest("hex"); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + await recordCapturedEntry( + { path: relative, hash: hash6, size, type: "file" }, + { kind: "symlink", target, rawTarget: firstTarget, stat: after } + ); + }; + const revalidateCapturedEntries = async () => { + for (const [relative, validation2] of [...capturedEntryValidations]) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (validation2.kind === "file") { + let realTarget2; + let stat2; + try { + realTarget2 = await fs10.realpath(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + stat2 = await fs10.lstat(validation2.target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "file", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (realTarget2 !== validation2.realTarget || !stat2.isFile() || stat2.isSymbolicLink() || !sameFileIdentity4(validation2.stat, stat2) || stat2.size !== validation2.stat.size || stat2.mtimeMs !== validation2.stat.mtimeMs || stat2.ctimeMs !== validation2.stat.ctimeMs) { + invalidateCapturedEntry(relative, { + path: relative, + size: stat2.isFile() ? stat2.size : null, + type: stat2.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + } + continue; + } + if (validation2.kind === "symlink") { + let stat2; + let rawTarget; + try { + stat2 = await fs10.lstat(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + rawTarget = await fs10.readlink(validation2.target, { encoding: "buffer" }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "other", + reason: isUnreadableError(error) ? "unreadable" : "changed-during-read" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (!stat2.isSymbolicLink() || !sameFileIdentity4(validation2.stat, stat2) || stat2.mtimeMs !== validation2.stat.mtimeMs || stat2.ctimeMs !== validation2.stat.ctimeMs || !validation2.rawTarget.equals(rawTarget)) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "other", + reason: "changed-during-read" + }); + } + continue; + } + let realTarget; + let stat; + try { + realTarget = await fs10.realpath(validation2.target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + stat = await fs10.lstat(validation2.target); + } catch { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + const validBoundary = realTarget === validation2.realTarget && stat.isDirectory() && !stat.isSymbolicLink() && sameFileIdentity4(validation2.stat, stat) && isInsidePath(physicalProjectRoot, realTarget) && !sameOrInside(physicalNativeRoot, realTarget); + if (!validBoundary) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-changed" + }); + continue; + } + let workingTree; + try { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + workingTree = await inspectGitlinkWorkingTree(execution, realTarget); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (workingTree.dirty) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-dirty" + }); + } else if (workingTree.hash !== validation2.hash) { + invalidateCapturedEntry(relative, { + path: relative, + size: null, + type: "directory", + reason: "gitlink-changed" + }); + } + } + for (const [relative, target] of capturedTrackedAbsences) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + try { + const stat = await fs10.lstat(target); + if (!nativeSnapshotExecutionHasBudget(execution)) return; + omit({ + path: relative, + size: stat.isFile() ? stat.size : null, + type: stat.isFile() ? "file" : stat.isDirectory() ? "directory" : "other", + reason: "changed-during-read" + }); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) return; + if (isChangedDuringReadError(error)) continue; + if (!isUnreadableError(error)) throw error; + omit({ path: relative, size: null, type: "file", reason: "unreadable" }); + } + } + }; + const gitSelection = await nativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + options.gitSelectionHooks + ); + if (gitSelection === null) { + const before = await nativePhysicalSnapshotSelection({ + execution, + paths, + physicalProjectRoot, + physicalNativeRoot, + denylist, + limits: physicalSelectionLimits, + hooks: options.physicalSelectionHooks + }); + await options.physicalSelectionHooks?.afterInitialSelection?.(); + for (const record8 of before.records) { + if (record8.type !== "file" && record8.type !== "symlink") continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + if (!snapshotPolicyIncludes(policy, record8.path, execution)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + const target = path10.resolve(projectRoot, ...record8.path.split("/")); + let stat; + try { + stat = await fs10.lstat(target); + } catch (error) { + if (!nativeSnapshotExecutionHasBudget(execution)) break; + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: record8.path, + size: null, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + continue; + } + if (remainingNativeSnapshotTime(execution) < 1) break; + if (record8.type === "symlink" && stat.isSymbolicLink()) { + await captureSymbolicLink(target, record8.path, stat); + if (remainingNativeSnapshotTime(execution) < 1) break; + continue; + } + if (record8.type !== "file" || !stat.isFile() || stat.isSymbolicLink()) { + omit({ + path: record8.path, + size: stat.isFile() ? stat.size : null, + type: stat.isDirectory() ? "directory" : stat.isFile() ? "file" : "other", + reason: "changed-during-read" + }); + continue; + } + await captureFile(target, record8.path, stat); + if (remainingNativeSnapshotTime(execution) < 1) break; + } + if (remainingNativeSnapshotTime(execution) >= 1) await revalidateCapturedEntries(); + const after = await nativePhysicalSnapshotSelection({ + execution, + paths, + physicalProjectRoot, + physicalNativeRoot, + denylist, + limits: physicalSelectionLimits + }); + const finalized = finalizeNativePhysicalSelection(before.evidence, after.evidence); + physicalSelectionEvidence = finalized.evidence; + for (const omission of [...before.omissions, ...after.omissions, ...finalized.omissions]) { + if (omitted.some( + (current) => current.path === omission.path && current.reason === omission.reason + )) { + continue; + } + omit(omission); + } + } else { + await options.gitSelectionHooks?.afterInitialSelection?.(); + for (const relative of selectionPaths(gitSelection)) { + if (!isSnapshotProjectRef(paths, relative)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + if (!snapshotPolicyIncludes(policy, relative, execution)) continue; + if (remainingNativeSnapshotTime(execution) < 1) break; + const target = path10.resolve(projectRoot, ...relative.split("/")); + if (target === configFile || target === selectionFile || denylist.some((denied) => sameOrInside(denied, target))) { + continue; + } + if (gitSelection.gitlinks.has(relative)) { + if (entries.length >= limits.maxFiles) { + omit({ path: relative, size: 0, type: "file", reason: "file-count" }); + continue; + } + let realGitlink; + let gitlinkStat; + try { + [realGitlink, gitlinkStat] = await Promise.all([fs10.realpath(target), fs10.lstat(target)]); + } catch { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + if (gitlinkStat.isSymbolicLink() || !gitlinkStat.isDirectory() || !isInsidePath(physicalProjectRoot, realGitlink) || sameOrInside(physicalNativeRoot, realGitlink)) { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + continue; + } + try { + const workingTree = await inspectGitlinkWorkingTree(execution, realGitlink); + if (workingTree.dirty) { + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-dirty" + }); + continue; + } + await recordCapturedEntry( + { + path: relative, + hash: workingTree.hash, + size: 0, + type: "file" + }, + { + kind: "gitlink", + target, + realTarget: realGitlink, + stat: gitlinkStat, + hash: workingTree.hash + } + ); + } catch (error) { + if (isNativeGitSnapshotTimeout(error)) throw error; + omit({ + path: relative, + size: null, + type: "directory", + reason: "gitlink-unavailable" + }); + } + continue; + } + let before; + try { + before = await fs10.lstat(target); + } catch (error) { + if (isChangedDuringReadError(error) && gitSelection.tracked.has(relative)) { + capturedTrackedAbsences.set(relative, target); + continue; + } + if (!isUnreadableError(error) && !isChangedDuringReadError(error)) throw error; + omit({ + path: relative, + size: null, + type: "file", + reason: isChangedDuringReadError(error) ? "changed-during-read" : "unreadable" + }); + continue; + } + if (before.isSymbolicLink()) { + await captureSymbolicLink(target, relative, before); + continue; + } + if (!before.isFile()) { + if (!gitSelection.nestedRepositories.has(relative)) { + omit({ path: relative, size: null, type: "other", reason: "changed-during-read" }); + } + continue; + } + await captureFile(target, relative, before); + } + await revalidateCapturedEntries(); + await finalizeNativeGitSnapshotSelection( + execution, + projectRoot, + gitSelectionLimits, + gitSelection, + options.gitSelectionHooks?.outputChunkBytes + ); + for (const omission of gitSelection.omissions) omit(omission); + if (gitSelection.overflow) foldGitSelectionOverflow(gitSelection.overflow); + } + entries.sort((left, right) => left.path.localeCompare(right.path, "en")); + omitted.sort((left, right) => left.path.localeCompare(right.path, "en")); + const capture = gitSelection === null ? { + provider: "physical-tree", + ...physicalSelectionEvidence ? { physicalSelection: physicalSelectionEvidence } : {} + } : { + provider: "git", + ...gitSelection.evidence ? { gitSelection: gitSelection.evidence } : {} + }; + const buildManifest = () => ({ + schema: "comet.native.content-snapshot.v1", + origin: options.origin ?? "explicit", + capture, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + complete: omittedCount === 0, + limits, + ...policy ? { policy: policy.manifest } : {}, + entries, + omitted, + omittedCount, + ...overflowCount > 0 ? { + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${overflowHash}`, + hash: overflowHash, + count: overflowCount + } + } : {} + }); + let manifest = buildManifest(); + while (serializedManifestBytes(manifest) > limits.maxManifestBytes) { + const compactableOmissionCount = omitted.filter( + (omission) => !isSelectionIntegrityOmission(omission) + ).length; + if (compactableOmissionCount > 0) { + const removeCount = Math.max(1, Math.ceil(omitted.length / 4)); + for (let removed = 0; removed < removeCount; removed += 1) { + const omission = takeLastCompactableOmission(omitted); + if (omission === null) break; + foldOverflow(omission); + } + } else if (entries.length > 0) { + const removeCount = Math.max(1, Math.ceil(entries.length / 4)); + for (const entry2 of entries.splice(-removeCount)) { + omittedCount += 1; + foldManifestEntryOverflow(entry2); + } + } else { + throw new Error("Native snapshot manifest byte limit is too small for its metadata"); + } + manifest = buildManifest(); + } + return manifest; +} +async function createNativeCurrentContentSnapshot(paths, baseline, options = {}) { + const config = await readProjectConfig(paths.projectRoot); + const settings = config?.native.snapshot ?? DEFAULT_NATIVE_SNAPSHOT_CONFIG; + return createNativeContentSnapshot(paths, { + ...options, + policy: baseline.policy, + limits: { + maxFiles: settings.max_files, + maxFileBytes: settings.max_total_bytes, + maxTotalBytes: settings.max_total_bytes, + maxDurationMs: settings.max_duration_ms + }, + deadlineMs: settings.max_duration_ms + }); +} +async function writeNativeBaselineManifest(paths, name, manifest) { + const file = nativeBaselineManifestFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + await atomicWriteJson(file, parseNativeContentSnapshotManifest(manifest)); +} +async function readNativeBaselineManifest(paths, name) { + const file = nativeBaselineManifestFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const source = await readNativeProtectedTextFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES, + label: "Native baseline snapshot manifest" + }); + return parseNativeContentSnapshotManifest(JSON.parse(source.text)); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} + +// domains/comet-native/native-trajectory-recovery.ts +import { createHash as createHash7 } from "crypto"; +import path14 from "path"; + +// domains/engine/storage-layout.ts +import path11 from "path"; +var NATIVE_RUN_STORAGE = /* @__PURE__ */ Object.freeze({ + stateRef: "runtime/run-state.json", + pendingRef: "runtime/pending-action.json", + trajectoryRef: "runtime/trajectory.jsonl", + contextRef: "runtime/context.md", + artifactsRef: "runtime/artifacts.json", + checkpointRef: "runtime/checkpoints/latest.json", + snapshotsRef: "runtime/skill-snapshots" +}); +function assertRunStorageRef(value) { + if (value.length === 0 || path11.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error("Run storage ref must stay inside the Run root"); + } +} +function assertRunStorageLayout(storage) { + for (const value of Object.values(storage)) assertRunStorageRef(value); +} + +// domains/comet-native/native-run-store.ts +import { createHash as createHash6 } from "node:crypto"; +import { constants as fsConstants3, promises as fs11 } from "node:fs"; +import path13 from "node:path"; +import { TextDecoder as TextDecoder4 } from "node:util"; + +// domains/engine/state.ts +import path12 from "path"; +var field = (doc, key) => { + const value = doc[key]; + return value === null || value === void 0 ? null : String(value); +}; +function requiredString(doc, key) { + const value = doc[key]; + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Invalid Run state: ${key} must be a non-empty string`); + } + return value; +} +function requiredRunReference(doc, key) { + const value = requiredString(doc, key); + if (path12.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Run state: ${key} must stay inside the change directory`); + } + return value; +} +function retries(doc) { + const raw = doc.run_retries ?? "{}"; + let value; + try { + value = typeof raw === "string" ? JSON.parse(raw) : raw; + } catch (error) { + throw new Error("Invalid Run state: run_retries must be a JSON object", { cause: error }); + } + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: run_retries must be a JSON object"); + } + for (const count of Object.values(value)) { + if (!Number.isInteger(count) || Number(count) < 0) { + throw new Error("Invalid Run state: retry counts must be non-negative integers"); + } + } + return value; +} +function runStateFromDocument(doc) { + if (!doc.run_id) return null; + const runId = requiredString(doc, "run_id"); + const skill = requiredString(doc, "skill"); + const skillVersion = requiredString(doc, "skill_version"); + const skillHash = requiredString(doc, "skill_hash"); + const pendingRef = requiredRunReference(doc, "pending_ref"); + const trajectoryRef = requiredRunReference(doc, "trajectory_ref"); + const contextRef = requiredRunReference(doc, "context_ref"); + const artifactsRef = requiredRunReference(doc, "artifacts_ref"); + const checkpointRef = requiredRunReference(doc, "checkpoint_ref"); + const iteration = Number(doc.iteration); + if (!Number.isInteger(iteration) || iteration < 0) { + throw new Error("Invalid Run state: iteration must be a non-negative integer"); + } + if (doc.orchestration !== "deterministic" && doc.orchestration !== "adaptive") { + throw new Error("Invalid Run state: orchestration must be deterministic or adaptive"); + } + if (doc.run_status !== "running" && doc.run_status !== "waiting" && doc.run_status !== "completed" && doc.run_status !== "failed") { + throw new Error("Invalid Run state: run_status is invalid"); + } + return { + runId, + skill, + skillVersion, + skillHash, + orchestration: doc.orchestration, + currentStep: field(doc, "current_step"), + iteration, + pending: field(doc, "pending"), + pendingRef, + trajectoryRef, + contextRef, + artifactsRef, + checkpointRef, + status: doc.run_status, + retries: retries(doc) + }; +} + +// domains/engine/storage-run.ts +var STORED_RUN_STATE_KEYS = /* @__PURE__ */ new Set([ + "runId", + "skill", + "skillVersion", + "skillHash", + "orchestration", + "currentStep", + "iteration", + "pending", + "pendingRef", + "trajectoryRef", + "contextRef", + "artifactsRef", + "checkpointRef", + "status", + "retries" +]); +function fromStoredState(json) { + const document = { + run_id: json.runId, + skill: json.skill, + skill_version: json.skillVersion, + skill_hash: json.skillHash, + orchestration: json.orchestration, + current_step: json.currentStep, + iteration: json.iteration, + pending: json.pending, + pending_ref: json.pendingRef, + trajectory_ref: json.trajectoryRef, + context_ref: json.contextRef, + artifacts_ref: json.artifactsRef, + checkpoint_ref: json.checkpointRef, + run_status: json.status, + run_retries: JSON.stringify(json.retries) + }; + const parsed = runStateFromDocument(document); + if (!parsed) throw new Error("Invalid Run state: runId must be a non-empty string"); + return parsed; +} +function parseStoredRunStateValue(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: stored value must be an object"); + } + const json = value; + const unknown = Object.keys(json).find( + (key) => !STORED_RUN_STATE_KEYS.has(key) + ); + if (unknown) throw new Error(`Invalid Run state: unknown field ${unknown}`); + if (json.currentStep !== null && (typeof json.currentStep !== "string" || json.currentStep.length === 0)) { + throw new Error("Invalid Run state: currentStep must be a non-empty string or null"); + } + if (json.pending !== null && (typeof json.pending !== "string" || json.pending.length === 0)) { + throw new Error("Invalid Run state: pending must be a non-empty string or null"); + } + if (!json.retries || typeof json.retries !== "object" || Array.isArray(json.retries)) { + throw new Error("Invalid Run state: retries must be an object"); + } + return fromStoredState(json); +} +function startRunWithStorage(pkg, runId, skillHash, storage) { + assertRunStorageLayout(storage); + return { + runId, + skill: pkg.definition.metadata.name, + skillVersion: pkg.definition.metadata.version, + skillHash, + orchestration: pkg.definition.orchestration.mode, + currentStep: pkg.definition.orchestration.entry ?? null, + iteration: 0, + pending: null, + pendingRef: storage.pendingRef, + trajectoryRef: storage.trajectoryRef, + contextRef: storage.contextRef, + artifactsRef: storage.artifactsRef, + checkpointRef: storage.checkpointRef, + status: "running", + retries: {} + }; +} + +// domains/comet-native/native-run-store.ts +var NATIVE_RUN_IO_LIMITS = { + runStateBytes: 256 * 1024, + trajectoryBytes: 8 * 1024 * 1024, + trajectoryEvents: 4096, + trajectoryEventBytes: 256 * 1024, + checkpointBytes: 256 * 1024, + pendingActionBytes: 256 * 1024, + contextBytes: 1024 * 1024, + artifactsBytes: 1024 * 1024 +}; +function isInside4(parent, target) { + const relative = path13.relative(parent, target); + return relative === "" || !path13.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path13.sep}`); +} +function asIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity5(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +function sameDirectoryIdentity4(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function runFile(changeDir, kind, relativePath) { + const expected = NATIVE_RUN_STORAGE[kind]; + if (relativePath !== void 0 && relativePath !== expected) { + throw new Error(`Native Run ${kind} ref must be ${expected}`); + } + const root = path13.resolve(changeDir); + const target = path13.resolve(root, ...expected.split("/")); + if (!isInside4(root, target)) throw new Error("Native Run path must stay inside its change"); + return target; +} +async function directoryIdentity2(directory) { + const stat = await fs11.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native Run parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs11.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain3(root, directory) { + const lexicalRoot = path13.resolve(root); + const lexicalDirectory = path13.resolve(directory); + if (!isInside4(lexicalRoot, lexicalDirectory)) { + throw new Error("Native Run path is outside its change"); + } + const chain = [await directoryIdentity2(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path13.relative(lexicalRoot, lexicalDirectory).split(path13.sep).filter(Boolean)) { + cursor = path13.join(cursor, segment); + let identity; + try { + identity = await directoryIdentity2(cursor); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (!isInside4(chain[0].realPath, identity.realPath)) { + throw new Error(`Native Run parent resolves outside its change: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain4(chain) { + for (const identity of chain) { + const stat = await fs11.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity4(identity, stat) || await fs11.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native Run parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded2(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readProtectedText(changeDir, file, maxBytes, label, hooks) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) return null; + await hooks?.afterParentChainCaptured?.(); + let before; + try { + before = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asIdentity(before); + const beforeRealPath = await fs11.realpath(file); + if (!isInside4(chain[0].realPath, beforeRealPath)) { + throw new Error(`${label} resolves outside its change`); + } + const flags = process.platform === "win32" ? fsConstants3.O_RDONLY : fsConstants3.O_RDONLY | fsConstants3.O_NOFOLLOW | fsConstants3.O_NONBLOCK; + let handle; + try { + handle = await fs11.open(file, flags); + } catch (error) { + throw new Error(`${label} changed while opening`, { cause: error }); + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs11.lstat(file), + fs11.realpath(file) + ]); + await verifyDirectoryChain4(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity5(beforeIdentity, opened) || !sameFileIdentity5(beforeIdentity, pathAfterOpen)) { + throw new Error(`${label} changed while opening`); + } + await hooks?.afterOpen?.(); + const bytes = await readHandleBounded2(handle, maxBytes, label); + await hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs11.lstat(file), + fs11.realpath(file) + ]); + await verifyDirectoryChain4(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity5(beforeIdentity, afterHandle) || !sameFileIdentity5(beforeIdentity, afterPath)) { + throw new Error(`${label} changed while reading`); + } + let text; + try { + text = new TextDecoder4("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`${label} is not valid UTF-8`, { cause: error }); + } + return { + text, + target: { exists: true, identity: beforeIdentity, realPath: beforeRealPath } + }; + } finally { + await handle.close(); + } +} +async function captureTarget(changeDir, file, label) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) return { exists: false }; + let stat; + try { + stat = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return { exists: false }; + throw error; + } + if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`${label} must be a regular file`); + const realPath = await fs11.realpath(file); + if (!isInside4(chain[0].realPath, realPath)) + throw new Error(`${label} resolves outside its change`); + await verifyDirectoryChain4(chain); + return { exists: true, identity: asIdentity(stat), realPath }; +} +async function verifyTarget(changeDir, file, expected, label) { + const chain = await captureDirectoryChain3(changeDir, path13.dirname(file)); + if (!chain) { + if (!expected.exists) return; + throw new Error(`${label} parent changed before commit`); + } + let stat; + try { + stat = await fs11.lstat(file); + } catch (error) { + if (error.code === "ENOENT" && !expected.exists) return; + throw new Error(`${label} changed before commit`, { cause: error }); + } + if (!expected.exists || !stat.isFile() || stat.isSymbolicLink() || !sameFileIdentity5(expected.identity, stat) || await fs11.realpath(file) !== expected.realPath) { + throw new Error(`${label} changed before commit`); + } + await verifyDirectoryChain4(chain); +} +async function writeProtectedText(options) { + const bytes = Buffer.byteLength(options.content, "utf8"); + if (bytes > options.maxBytes) { + throw new Error(`${options.label} exceeds ${options.maxBytes} bytes`); + } + const expected = options.expected ?? await captureTarget(options.changeDir, options.file, options.label); + if (expected.exists && expected.identity.size > options.maxBytes) { + throw new Error(`${options.label} exceeds ${options.maxBytes} bytes`); + } + await atomicWriteText(options.file, options.content, { + containedRoot: options.changeDir, + beforeCommit: async () => { + await options.hooks?.beforeCommit?.(); + await verifyTarget(options.changeDir, options.file, expected, options.label); + } + }); + const persisted = await readProtectedText( + options.changeDir, + options.file, + options.maxBytes, + options.label + ); + if (!persisted || persisted.text !== options.content) { + throw new Error(`${options.label} commit could not be verified`); + } +} +function parseJson(text, label) { + try { + return JSON.parse(text); + } catch (error) { + throw new Error(`${label} contains invalid JSON`, { cause: error }); + } +} +function jsonText(value) { + return `${JSON.stringify(value, null, 2)} +`; +} +function parseNativeStoredRunStateValue(value) { + return parseStoredRunStateValue(value); +} +function startNativeRun(pkg, runId, skillHash) { + return startRunWithStorage(pkg, runId, skillHash, NATIVE_RUN_STORAGE); +} +async function readNativeRunState(changeDir, hooks) { + const file = runFile(changeDir, "stateRef"); + const raw = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.runStateBytes, + "Native Run state", + hooks + ); + return raw ? parseStoredRunStateValue(parseJson(raw.text, "Native Run state")) : null; +} +async function writeNativeRunState(changeDir, state, hooks) { + const validated = parseStoredRunStateValue(state); + await writeProtectedText({ + changeDir, + file: runFile(changeDir, "stateRef"), + content: jsonText(validated), + maxBytes: NATIVE_RUN_IO_LIMITS.runStateBytes, + label: "Native Run state", + hooks + }); +} +async function readNativeTrajectory(changeDir, relativePath, hooks) { + const file = runFile(changeDir, "trajectoryRef", relativePath); + const raw = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory", + hooks + ); + if (!raw) return []; + const lines = raw.text.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter(({ line }) => line.length > 0); + if (lines.length > NATIVE_RUN_IO_LIMITS.trajectoryEvents) { + throw new Error( + `Native Run trajectory exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEvents} events` + ); + } + return lines.map(({ line, number }) => { + try { + return JSON.parse(line); + } catch (error) { + throw new Error(`Invalid Native Run trajectory event at line ${number}`, { cause: error }); + } + }); +} +async function readNativeTrajectoryText(changeDir, relativePath, hooks) { + const raw = await readProtectedText( + changeDir, + runFile(changeDir, "trajectoryRef", relativePath), + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory", + hooks + ); + return raw?.text ?? null; +} +async function replaceNativeTrajectoryText(changeDir, relativePath, content, expectedHash, hooks) { + if (!/^[a-f0-9]{64}$/u.test(expectedHash)) { + throw new Error("Native Run trajectory expected hash is invalid"); + } + const file = runFile(changeDir, "trajectoryRef", relativePath); + const existing = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory" + ); + if (!existing) throw new Error("Native Run trajectory disappeared before repair"); + const actualHash = createHash6("sha256").update(existing.text, "utf8").digest("hex"); + if (actualHash !== expectedHash) { + throw new Error("Native Run trajectory changed before repair"); + } + await writeProtectedText({ + changeDir, + file, + content, + maxBytes: NATIVE_RUN_IO_LIMITS.trajectoryBytes, + label: "Native Run trajectory", + expected: existing.target, + hooks + }); +} +async function appendNativeTrajectory(changeDir, relativePath, event, hooks) { + const file = runFile(changeDir, "trajectoryRef", relativePath); + const line = `${JSON.stringify(event)} +`; + if (Buffer.byteLength(line, "utf8") > NATIVE_RUN_IO_LIMITS.trajectoryEventBytes) { + throw new Error( + `Native Run trajectory event exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEventBytes} bytes` + ); + } + const existing = await readProtectedText( + changeDir, + file, + NATIVE_RUN_IO_LIMITS.trajectoryBytes, + "Native Run trajectory" + ); + const existingEvents = existing ? existing.text.split(/\r?\n/u).filter((value) => value.length > 0).length : 0; + if (existingEvents >= NATIVE_RUN_IO_LIMITS.trajectoryEvents) { + throw new Error( + `Native Run trajectory exceeds ${NATIVE_RUN_IO_LIMITS.trajectoryEvents} events` + ); + } + await writeProtectedText({ + changeDir, + file, + content: `${existing?.text ?? ""}${line}`, + maxBytes: NATIVE_RUN_IO_LIMITS.trajectoryBytes, + label: "Native Run trajectory", + expected: existing?.target ?? { exists: false }, + hooks + }); +} +async function readNativeCheckpoint(changeDir, relativePath, hooks) { + const raw = await readProtectedText( + changeDir, + runFile(changeDir, "checkpointRef", relativePath), + NATIVE_RUN_IO_LIMITS.checkpointBytes, + "Native Run checkpoint", + hooks + ); + return raw ? parseJson(raw.text, "Native Run checkpoint") : null; +} +async function writeNativeCheckpoint(changeDir, relativePath, checkpoint, hooks) { + await writeProtectedText({ + changeDir, + file: runFile(changeDir, "checkpointRef", relativePath), + content: jsonText(checkpoint), + maxBytes: NATIVE_RUN_IO_LIMITS.checkpointBytes, + label: "Native Run checkpoint", + hooks + }); +} + +// domains/comet-native/native-trajectory-recovery.ts +var CHANGE_NAME_PATTERN2 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var NativeTrajectoryRepairRequiredError = class extends Error { + constructor(inspection) { + super( + inspection.status === "repairable" ? `Native trajectory has an incomplete final line at ${inspection.file}:${inspection.line}; run doctor --repair` : `Native trajectory is invalid at ${inspection.file}:${inspection.line}: ${inspection.message}` + ); + this.inspection = inspection; + this.name = "NativeTrajectoryRepairRequiredError"; + } + inspection; + code = "native-trajectory-tail-repair-required"; +}; +function sha256Buffer(value) { + return createHash7("sha256").update(value).digest("hex"); +} +function trajectoryFile(paths, name) { + if (!CHANGE_NAME_PATTERN2.test(name)) throw new Error(`Invalid Native change name: ${name}`); + const changeDir = path14.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, changeDir)) throw new Error("Native change path escaped"); + return path14.join(changeDir, "runtime", "trajectory.jsonl"); +} +function parseCompleteLines(content) { + const lines = content.split(/\n/u); + let count = 0; + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index].endsWith("\r") ? lines[index].slice(0, -1) : lines[index]; + if (line.length === 0) continue; + count = index + 1; + try { + JSON.parse(line); + } catch (error) { + return { status: "invalid", line: index + 1, message: error.message }; + } + } + return { status: "valid", count }; +} +function looksTruncated(error, content) { + if (/Unexpected end|Unterminated string/iu.test(error.message)) return true; + const position = /position (\d+)/iu.exec(error.message)?.[1]; + return position !== void 0 && Number(position) >= Math.max(0, content.length - 1); +} +function analyzeTrajectory(file, source) { + const lastNewline = source.lastIndexOf(10); + const prefix = source.subarray(0, lastNewline + 1); + const complete = parseCompleteLines(prefix.toString("utf8")); + if (complete.status === "invalid") { + return { status: "invalid", file, line: complete.line, message: complete.message }; + } + if (lastNewline === source.length - 1) return { status: "clean", file }; + const tail = source.subarray(lastNewline + 1); + const tailText = tail.toString("utf8"); + const line = complete.count + 1; + let reason; + let target; + try { + JSON.parse(tailText.endsWith("\r") ? tailText.slice(0, -1) : tailText); + reason = "missing-newline"; + target = Buffer.concat([source, Buffer.from("\n")]); + } catch (error) { + if (!looksTruncated(error, tailText)) { + return { status: "invalid", file, line, message: error.message }; + } + reason = "incomplete-json"; + target = prefix; + } + const inspection = { + status: "repairable", + file, + reason, + line, + originalHash: sha256Buffer(source), + targetHash: sha256Buffer(target), + tailHash: sha256Buffer(tail), + discardedBytes: reason === "incomplete-json" ? tail.length : 0 + }; + return { inspection, targetContent: target.toString("utf8") }; +} +async function inspectFile(paths, name) { + const file = trajectoryFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + const changeDir = path14.join(paths.changesDir, name); + const content = await readNativeTrajectoryText(changeDir, NATIVE_RUN_STORAGE.trajectoryRef); + return content === null ? { status: "clean", file } : analyzeTrajectory(file, Buffer.from(content)); +} +async function inspectNativeTrajectoryTail(paths, name) { + const result2 = await inspectFile(paths, name); + return "inspection" in result2 ? result2.inspection : result2; +} +async function assertNativeTrajectoryHealthy(paths, name) { + const inspection = await inspectNativeTrajectoryTail(paths, name); + if (inspection.status !== "clean") throw new NativeTrajectoryRepairRequiredError(inspection); +} +async function repairNativeTrajectoryTail(paths, name, hooks) { + return withNativeMutationLock(paths, `repair trajectory tail for ${name}`, async () => { + const result2 = await inspectFile(paths, name); + if (!("inspection" in result2)) { + if (result2.status === "clean") return null; + throw new NativeTrajectoryRepairRequiredError(result2); + } + try { + await replaceNativeTrajectoryText( + path14.join(paths.changesDir, name), + NATIVE_RUN_STORAGE.trajectoryRef, + result2.targetContent, + result2.inspection.originalHash, + { beforeCommit: hooks?.beforeCommit } + ); + } catch (error) { + if (/changed (?:before|while)/iu.test(error.message)) { + throw new Error( + `Native trajectory changed while preparing tail repair for ${name}; inspect it again before retrying`, + { cause: error } + ); + } + throw error; + } + const repaired = await inspectNativeTrajectoryTail(paths, name); + if (repaired.status !== "clean") { + throw new Error(`Native trajectory tail repair did not produce a clean file for ${name}`); + } + return result2.inspection; + }); +} + +// domains/comet-native/native-workspace.ts +import { createHash as createHash8 } from "node:crypto"; +import { promises as fs12 } from "node:fs"; +import path15 from "node:path"; +var HASH_PATTERN2 = /^[a-f0-9]{64}$/u; +var MAX_WORKSPACE_IDENTITY_BYTES = 16 * 1024; +var NATIVE_WORKSPACE_ADVISORY_CODES = /* @__PURE__ */ new Set([ + "workspace-root-changed", + "workspace-inspection-unavailable" +]); +function isNativeWorkspaceAdvisoryCode(code) { + return NATIVE_WORKSPACE_ADVISORY_CODES.has(code); +} +function portableRelative2(parent, target) { + const relative = path15.relative(parent, target); + if (path15.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path15.sep}`)) { + return null; + } + return relative.replaceAll("\\", "/") || "."; +} +function hasControlCharacter(value) { + return Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }); +} +function normalizedPortableRef(value, label) { + if (value.length === 0 || hasControlCharacter(value) || value.includes("\\") || path15.posix.isAbsolute(value) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..")) { + throw new Error(`${label} must be a portable project-relative path`); + } + const normalized = path15.posix.normalize(value); + if (normalized !== value || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return normalized; +} +function identityHash(tag, value) { + return createHash8("sha256").update(`${tag} +${value}`).digest("hex"); +} +async function physicalDirectoryIdentity(tag, value) { + const realPath = await fs12.realpath(value); + const stat = await fs12.lstat(realPath); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error("Native workspace identity requires a real directory"); + } + const normalizedPath = process.platform === "win32" ? path15.normalize(realPath).toLowerCase() : realPath; + return identityHash(tag, `${normalizedPath} +${stat.dev} +${stat.ino} +${stat.birthtimeMs}`); +} +async function directoryPathIdentity(tag, value) { + const realPath = await fs12.realpath(value); + const stat = await fs12.lstat(realPath); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error("Native workspace identity requires a real directory"); + } + const normalizedPath = process.platform === "win32" ? path15.normalize(realPath).toLowerCase() : realPath; + return identityHash(tag, normalizedPath); +} +function isoTimestamp(value) { + if (typeof value !== "string") throw new Error("Native workspace capturedAt is invalid"); + const parsed = new Date(value); + if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) { + throw new Error("Native workspace capturedAt is invalid"); + } + return value; +} +function assertIdentity(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native workspace identity must be an object"); + } + const root = value; + const allowed = /* @__PURE__ */ new Set([ + "schema", + "capturedAt", + "capturedRevision", + "nativeRootRef", + "projectRootId", + "nativeRootId", + "projectRootPathId", + "nativeRootPathId", + "sessionHash" + ]); + const unknown = Object.keys(root).filter((key) => !allowed.has(key)); + if (unknown.length > 0) { + throw new Error(`Native workspace identity has unknown field(s): ${unknown.join(", ")}`); + } + if (root.schema !== "comet.native.workspace.v2") { + throw new Error("Unsupported Native workspace identity"); + } + if (!Number.isSafeInteger(root.capturedRevision) || root.capturedRevision < 1 || typeof root.nativeRootRef !== "string" || !HASH_PATTERN2.test(String(root.projectRootId)) || !HASH_PATTERN2.test(String(root.nativeRootId))) { + throw new Error("Native workspace identity is invalid"); + } + isoTimestamp(root.capturedAt); + normalizedPortableRef(root.nativeRootRef, "Native workspace root ref"); + const hasProjectPathId = root.projectRootPathId !== void 0; + const hasNativePathId = root.nativeRootPathId !== void 0; + if (hasProjectPathId !== hasNativePathId) { + throw new Error("Native workspace path identities must be provided together"); + } + if (hasProjectPathId && !HASH_PATTERN2.test(String(root.projectRootPathId)) || hasNativePathId && !HASH_PATTERN2.test(String(root.nativeRootPathId))) { + throw new Error("Native workspace path identity is invalid"); + } + if (root.sessionHash !== void 0 && !HASH_PATTERN2.test(String(root.sessionHash))) { + throw new Error("Native workspace session hash is invalid"); + } +} +function nativeWorkspaceFile(paths, name) { + return path15.join(paths.changesDir, name, "runtime", "workspace.json"); +} +function nativeWorkspaceRef(paths, name) { + const relative = portableRelative2(paths.nativeRoot, nativeWorkspaceFile(paths, name)); + if (!relative || relative === ".") throw new Error("Native workspace file escaped its root"); + return normalizedPortableRef(relative, "Native workspace file ref"); +} +async function readNativeWorkspaceValue(paths, name) { + try { + const artifact = await readNativeBoundedTextFile({ + root: paths.nativeRoot, + ref: nativeWorkspaceRef(paths, name), + maxBytes: MAX_WORKSPACE_IDENTITY_BYTES + }); + return JSON.parse(artifact.text); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function nativeWorkspaceIdentityNeedsMigration(paths, name) { + const value = await readNativeWorkspaceValue(paths, name); + if (value === null) return false; + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native workspace identity must be an object"); + } + if (value.schema === "comet.native.workspace.v1") return true; + assertIdentity(value); + return value.projectRootPathId === void 0 || value.nativeRootPathId === void 0; +} +async function inspectNativeWorkspaceIdentity(options) { + if (!Number.isSafeInteger(options.revision) || options.revision < 1) { + throw new Error("Native workspace revision must be a positive integer"); + } + const nativeRootRef3 = portableRelative2(options.paths.projectRoot, options.paths.nativeRoot); + if (!nativeRootRef3) throw new Error("Native root is outside the project root"); + const [projectRootId, nativeRootId, projectRootPathId, nativeRootPathId] = await Promise.all([ + physicalDirectoryIdentity("comet.native.workspace-project-root.v2", options.paths.projectRoot), + physicalDirectoryIdentity("comet.native.workspace-native-root.v2", options.paths.nativeRoot), + directoryPathIdentity("comet.native.workspace-project-root-path.v2", options.paths.projectRoot), + directoryPathIdentity("comet.native.workspace-native-root-path.v2", options.paths.nativeRoot) + ]); + const capturedAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString(); + const identity = { + schema: "comet.native.workspace.v2", + capturedAt, + capturedRevision: options.revision, + nativeRootRef: nativeRootRef3, + projectRootId, + nativeRootId, + projectRootPathId, + nativeRootPathId, + ...options.sessionId ? { + sessionHash: identityHash( + "comet.native.workspace-session.v2", + `${projectRootId} +${nativeRootId} +${options.sessionId}` + ) + } : {} + }; + assertIdentity(identity); + return identity; +} +async function writeNativeWorkspaceIdentity(options) { + const identity = await inspectNativeWorkspaceIdentity(options); + const file = nativeWorkspaceFile(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, file); + await atomicWriteJson(file, identity, { containedRoot: options.paths.nativeRoot }); + return identity; +} +async function readNativeWorkspaceIdentity(paths, name) { + const value = await readNativeWorkspaceValue(paths, name); + if (value === null) return null; + if (value && typeof value === "object" && !Array.isArray(value) && value.schema === "comet.native.workspace.v1") { + return null; + } + assertIdentity(value); + return value; +} +async function migrateLegacyNativeWorkspaceIdentity(options) { + if (!await nativeWorkspaceIdentityNeedsMigration(options.paths, options.name)) { + return null; + } + return writeNativeWorkspaceIdentity(options); +} +async function inspectNativeWorkspaceAdvisory(options) { + assertIdentity(options.identity); + const current = await inspectNativeWorkspaceIdentity({ + paths: options.paths, + name: "workspace-advisory", + revision: options.identity.capturedRevision + }); + const driftComponents = []; + const codes = []; + if (current.nativeRootRef !== options.identity.nativeRootRef) { + driftComponents.push("native-root-ref"); + } + if (options.identity.projectRootPathId && options.identity.nativeRootPathId) { + if (current.projectRootPathId !== options.identity.projectRootPathId) { + driftComponents.push("project-root-path"); + } + if (current.nativeRootPathId !== options.identity.nativeRootPathId) { + driftComponents.push("native-root-path"); + } + } else { + if (current.projectRootId !== options.identity.projectRootId) { + driftComponents.push("project-root-legacy-identity"); + } + if (current.nativeRootId !== options.identity.nativeRootId) { + driftComponents.push("native-root-legacy-identity"); + } + } + const onlyUnstableWindowsLegacyHashes = process.platform === "win32" && driftComponents.length > 0 && driftComponents.every( + (component) => component === "project-root-legacy-identity" || component === "native-root-legacy-identity" + ); + if (onlyUnstableWindowsLegacyHashes) { + codes.push("workspace-inspection-unavailable"); + } else if (driftComponents.length > 0) { + codes.push("workspace-root-changed"); + } + return { + state: codes.length === 0 ? "aligned" : codes.includes("workspace-root-changed") ? "drifted" : "unknown", + findingCodes: codes, + driftComponents + }; +} + +// domains/comet-native/native-types.ts +var NATIVE_RUNTIME_PROTOCOL_VERSION = 3; +var NATIVE_CHANGE_SCHEMA = "comet.native.v3"; +var NATIVE_V2_CHANGE_SCHEMA = "comet.native.v2"; +var NATIVE_LEGACY_CHANGE_SCHEMA = "comet.native.v1"; +var NATIVE_TRANSITION_SCHEMA = "comet.native.transition.v3"; +var NATIVE_V2_TRANSITION_SCHEMA = "comet.native.transition.v2"; +var NATIVE_LEGACY_TRANSITION_SCHEMA = "comet.native.transition.v1"; + +// domains/comet-native/native-change.ts +var CHANGE_KEYS = [ + "schema", + "name", + "language", + "phase", + "brief", + "approval", + "spec_changes", + "verification_result", + "verification_report", + "archived", + "created_at", + "run_id" +]; +var LEGACY_CHANGE_KEYS = new Set(CHANGE_KEYS); +var V2_CHANGE_KEYS = /* @__PURE__ */ new Set([...CHANGE_KEYS, "minimum_runtime_version", "revision"]); +var CURRENT_CHANGE_KEYS = /* @__PURE__ */ new Set([ + ...V2_CHANGE_KEYS, + "approved_contract_hash", + "implementation_scope", + "verification_evidence", + "partial_allowance" +]); +var SPEC_CHANGE_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "source", "base_hash"]); +var PHASES = /* @__PURE__ */ new Set(["shape", "build", "verify", "archive"]); +var APPROVALS = /* @__PURE__ */ new Set(["implicit", "confirmed"]); +var VERIFY_RESULTS = /* @__PURE__ */ new Set(["pending", "pass", "fail"]); +var NATIVE_CHANGE_STATE_FILE = "comet-state.yaml"; +var HASH_PATTERN3 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CONTENT_ADDRESSED_REF_PATTERN = /^runtime\/evidence\/(scopes|allowances|verifications)\/([a-f0-9]{64})\.json$/u; +var NativeSchemaMigrationRequiredError = class extends Error { + constructor(change, schema) { + super( + `Native change ${change} uses ${schema}; run comet native doctor ${change} --repair before mutating it` + ); + this.change = change; + this.schema = schema; + this.name = "NativeSchemaMigrationRequiredError"; + } + change; + schema; + code = "native-schema-migration-required"; +}; +var NativeRuntimeCompatibilityError = class extends Error { + constructor(schema, minimumRuntimeVersion) { + super( + schema !== NATIVE_CHANGE_SCHEMA || minimumRuntimeVersion === null ? `Unsupported Native change schema ${schema} for runtime protocol ${NATIVE_RUNTIME_PROTOCOL_VERSION}` : `Native change ${schema} requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + this.schema = schema; + this.minimumRuntimeVersion = minimumRuntimeVersion; + this.name = "NativeRuntimeCompatibilityError"; + } + schema; + minimumRuntimeVersion; + code = "native-runtime-incompatible"; +}; +var NativeChangeRevisionConflictError = class extends Error { + constructor(change, expectedRevision, actualRevision) { + super( + `Native change ${change} revision conflict: expected ${expectedRevision}, actual ${actualRevision}` + ); + this.change = change; + this.expectedRevision = expectedRevision; + this.actualRevision = actualRevision; + this.name = "NativeChangeRevisionConflictError"; + } + change; + expectedRevision; + actualRevision; + code = "native-change-revision-conflict"; +}; +var NativeBaselineIncompleteError = class extends Error { + constructor(change, omittedCount, omittedByReason, samplePaths, sampleTruncated, effectiveLimits = null, policyHash = null) { + super( + `Native change ${change} baseline is incomplete (${omittedCount} omitted entr${omittedCount === 1 ? "y" : "ies"}). Adjust native.snapshot scope or resource budgets in .comet/config.yaml, then retry.` + ); + this.change = change; + this.omittedCount = omittedCount; + this.omittedByReason = omittedByReason; + this.samplePaths = samplePaths; + this.sampleTruncated = sampleTruncated; + this.effectiveLimits = effectiveLimits; + this.policyHash = policyHash; + this.name = "NativeBaselineIncompleteError"; + } + change; + omittedCount; + omittedByReason; + samplePaths; + sampleTruncated; + effectiveLimits; + policyHash; + code = "native-baseline-incomplete"; +}; +var NATIVE_BRIEF_TEMPLATE = [ + "# Outcome", + "", + "# Scope", + "", + "# Non-goals", + "", + "# Acceptance examples", + "", + "# Constraints and invariants", + "", + "# Decisions", + "", + "# Open questions", + "", + "# Verification expectations", + "" +].join("\n"); +function record4(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown4(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function assertNativeName(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native change name: ${value}`); +} +function assertCapabilityId(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native capability id: ${value}`); +} +function assertRelativeRef(value, label) { + if (value.length === 0 || path16.isAbsolute(value) || /^(?:[A-Za-z]:|~|[\\/])/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native change`); + } +} +function parseSpecChange(value, index) { + const item = record4(value, `spec_changes[${index}]`); + rejectUnknown4(item, SPEC_CHANGE_KEYS, `spec_changes[${index}]`); + if (typeof item.capability !== "string") throw new Error("spec change capability is required"); + assertCapabilityId(item.capability); + if (item.operation !== "create" && item.operation !== "replace" && item.operation !== "remove") { + throw new Error(`Invalid spec operation for ${item.capability}`); + } + const source = item.source; + const baseHash = item.base_hash; + if (source !== void 0 && typeof source !== "string") { + throw new Error(`Spec source for ${item.capability} must be a string`); + } + if (typeof source === "string") assertRelativeRef(source, `Spec source for ${item.capability}`); + if (item.operation === "create") { + if (!source) throw new Error(`Create spec ${item.capability} requires source`); + if (baseHash !== null) + throw new Error(`Create spec ${item.capability} requires null base_hash`); + } else if (item.operation === "replace") { + if (!source) throw new Error(`Replace spec ${item.capability} requires source`); + if (typeof baseHash !== "string" || !HASH_PATTERN3.test(baseHash)) { + throw new Error(`Replace spec ${item.capability} requires a SHA-256 base_hash`); + } + } else { + if (source !== void 0) throw new Error(`Remove spec ${item.capability} forbids source`); + if (typeof baseHash !== "string" || !HASH_PATTERN3.test(baseHash)) { + throw new Error(`Remove spec ${item.capability} requires a SHA-256 base_hash`); + } + } + return { + capability: item.capability, + operation: item.operation, + ...typeof source === "string" ? { source } : {}, + base_hash: baseHash + }; +} +function validDate(value) { + if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) return false; + return (/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`)).toISOString().slice(0, 10) === value; +} +function parseChangeFields(root, knownKeys) { + rejectUnknown4(root, knownKeys, NATIVE_CHANGE_STATE_FILE); + if (typeof root.name !== "string") throw new Error("Native change name is required"); + assertNativeName(root.name); + if (root.language !== "en" && root.language !== "zh-CN") { + throw new Error("Native change language must be en or zh-CN"); + } + if (typeof root.phase !== "string" || !PHASES.has(root.phase)) { + throw new Error("Native change phase is invalid"); + } + if (root.brief !== "brief.md") throw new Error("Native change brief must be brief.md"); + if (root.approval !== null && !APPROVALS.has(root.approval)) { + throw new Error("Native change approval is invalid"); + } + if (!Array.isArray(root.spec_changes)) throw new Error("Native spec_changes must be an array"); + const specChanges = root.spec_changes.map(parseSpecChange); + const duplicates = specChanges.map((change) => change.capability).filter((capability, index, all) => all.indexOf(capability) !== index); + if (duplicates.length > 0) { + throw new Error( + `Duplicate Native capability operation: ${[...new Set(duplicates)].join(", ")}` + ); + } + if (typeof root.verification_result !== "string" || !VERIFY_RESULTS.has(root.verification_result)) { + throw new Error("Native verification_result is invalid"); + } + if (root.verification_report !== null && typeof root.verification_report !== "string") { + throw new Error("Native verification_report must be a string or null"); + } + if (typeof root.verification_report === "string") { + assertRelativeRef(root.verification_report, "Native verification_report"); + } + if (typeof root.archived !== "boolean") throw new Error("Native archived must be boolean"); + if (typeof root.created_at !== "string" || !validDate(root.created_at)) { + throw new Error("Native created_at must be a valid YYYY-MM-DD date"); + } + if (root.run_id !== null && (typeof root.run_id !== "string" || root.run_id.length === 0)) { + throw new Error("Native run_id must be a non-empty string or null"); + } + return { + name: root.name, + language: root.language, + phase: root.phase, + brief: "brief.md", + approval: root.approval, + spec_changes: specChanges, + verification_result: root.verification_result, + verification_report: root.verification_report, + archived: root.archived, + created_at: root.created_at, + run_id: root.run_id + }; +} +function parseLegacyNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_LEGACY_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_LEGACY_CHANGE_SCHEMA}`); + } + return { + schema: NATIVE_LEGACY_CHANGE_SCHEMA, + ...parseChangeFields(root, LEGACY_CHANGE_KEYS) + }; +} +function positiveInteger2(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function contentAddressedRef(value, label, kind) { + if (value === null) return null; + const match = typeof value === "string" ? CONTENT_ADDRESSED_REF_PATTERN.exec(value) : null; + if (!match || match[1] !== kind) { + throw new Error( + `${label} must be null or runtime/evidence/${kind}/.json relative to the Native change` + ); + } + return value; +} +function approvedContractHash(value) { + if (value === void 0 || value === null) return null; + if (typeof value !== "string" || !HASH_PATTERN3.test(value)) { + throw new Error("Native approved_contract_hash must be null or a SHA-256 hash"); + } + return value; +} +function parseV2NativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_V2_CHANGE_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native v2 minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error(`Native ${NATIVE_V2_CHANGE_SCHEMA} minimum_runtime_version must be 2`); + } + return { + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision: positiveInteger2(root.revision, "Native v2 revision"), + ...parseChangeFields(root, V2_CHANGE_KEYS) + }; +} +function parseNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA || root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const previous = root.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? parseLegacyNativeChangeValue(root) : parseV2NativeChangeValue(root); + throw new NativeSchemaMigrationRequiredError(previous.name, previous.schema); + } + throw new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + typeof root.minimum_runtime_version === "number" ? root.minimum_runtime_version : null + ); + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native ${root.schema} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger2(root.revision, "Native revision"); + const fields = parseChangeFields(root, CURRENT_CHANGE_KEYS); + const approvalHash = approvedContractHash(root.approved_contract_hash); + if (fields.approval === null && approvalHash !== null) { + throw new Error("Native approved_contract_hash requires an approval"); + } + return { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + ...fields, + approved_contract_hash: approvalHash, + implementation_scope: contentAddressedRef( + root.implementation_scope, + "Native implementation_scope", + "scopes" + ), + verification_evidence: contentAddressedRef( + root.verification_evidence, + "Native verification_evidence", + "verifications" + ), + partial_allowance: contentAddressedRef( + root.partial_allowance, + "Native partial_allowance", + "allowances" + ) + }; +} +function inspectNativeChangeValue(value) { + const root = record4(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + const state2 = parseLegacyNativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: 1, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const state2 = parseV2NativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: state2.minimum_runtime_version, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + const minimumRuntimeVersion2 = typeof root.minimum_runtime_version === "number" && Number.isSafeInteger(root.minimum_runtime_version) ? root.minimum_runtime_version : null; + return { + status: "runtime-incompatible", + schema: typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion: minimumRuntimeVersion2, + state: null, + message: new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion2 + ).message + }; + } + const minimumRuntimeVersion = positiveInteger2( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + return { + status: "runtime-incompatible", + schema: root.schema, + minimumRuntimeVersion, + state: null, + message: new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion).message + }; + } + const state = parseNativeChangeValue(root); + return { + status: "current", + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + state + }; +} +function nativeChangeDocument(state) { + const parsed = parseNativeChangeValue(state); + return { + schema: parsed.schema, + minimum_runtime_version: parsed.minimum_runtime_version, + revision: parsed.revision, + name: parsed.name, + language: parsed.language, + phase: parsed.phase, + brief: parsed.brief, + approval: parsed.approval, + approved_contract_hash: parsed.approved_contract_hash ?? null, + spec_changes: parsed.spec_changes.map((change) => ({ + capability: change.capability, + operation: change.operation, + ...change.source ? { source: change.source } : {}, + base_hash: change.base_hash + })), + verification_result: parsed.verification_result, + verification_report: parsed.verification_report, + implementation_scope: parsed.implementation_scope, + verification_evidence: parsed.verification_evidence, + partial_allowance: parsed.partial_allowance, + archived: parsed.archived, + created_at: parsed.created_at, + run_id: parsed.run_id + }; +} +function nativeV2ChangeDocument(state) { + const parsed = parseV2NativeChangeValue(state); + return { + schema: parsed.schema, + minimum_runtime_version: parsed.minimum_runtime_version, + revision: parsed.revision, + name: parsed.name, + language: parsed.language, + phase: parsed.phase, + brief: parsed.brief, + approval: parsed.approval, + spec_changes: parsed.spec_changes.map((change) => ({ + capability: change.capability, + operation: change.operation, + ...change.source ? { source: change.source } : {}, + base_hash: change.base_hash + })), + verification_result: parsed.verification_result, + verification_report: parsed.verification_report, + archived: parsed.archived, + created_at: parsed.created_at, + run_id: parsed.run_id + }; +} +function nativeChangeDir(paths, name) { + assertNativeName(name); + const target = path16.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, target)) throw new Error("Native change path escaped"); + return target; +} +async function hasPendingNativeSchemaMigration(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function hasPendingNativeCheckpointRecovery(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), "runtime", "checkpoint-journal.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function createNativeChange(options) { + return withNativeMutationLock( + options.paths, + `create change ${options.name}`, + () => createNativeChangeLocked(options) + ); +} +async function createNativeChangeLocked(options) { + assertNativeName(options.name); + const changeDir = nativeChangeDir(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, changeDir); + let createdChangeDir = false; + try { + try { + await fs13.mkdir(changeDir, { recursive: false }); + createdChangeDir = true; + } catch (error) { + if (error.code === "ENOENT") { + await fs13.mkdir(options.paths.changesDir, { recursive: true }); + try { + await fs13.mkdir(changeDir, { recursive: false }); + createdChangeDir = true; + } catch (retryError) { + if (retryError.code === "EEXIST") { + throw new Error(`Native change already exists: ${options.name}`, { + cause: retryError + }); + } + throw retryError; + } + } else if (error.code === "EEXIST") { + throw new Error(`Native change already exists: ${options.name}`, { cause: error }); + } else { + throw error; + } + } + const state = { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + name: options.name, + language: options.language, + phase: "shape", + brief: "brief.md", + approval: null, + approved_contract_hash: null, + spec_changes: [], + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: false, + created_at: (options.now ?? /* @__PURE__ */ new Date()).toISOString().slice(0, 10), + run_id: null + }; + await Promise.all([ + fs13.mkdir(path16.join(changeDir, "specs"), { recursive: true }), + fs13.mkdir(path16.join(changeDir, "runtime", "checkpoints"), { recursive: true }), + atomicWriteText(path16.join(changeDir, "brief.md"), NATIVE_BRIEF_TEMPLATE) + ]); + const projectConfig = await readProjectConfig(options.paths.projectRoot); + const snapshot2 = projectConfig?.native.snapshot ?? DEFAULT_NATIVE_SNAPSHOT_CONFIG; + const baseline = await createNativeContentSnapshot(options.paths, { + now: options.now, + origin: "change-created", + policy: snapshot2, + limits: { + maxFiles: snapshot2.max_files, + maxFileBytes: snapshot2.max_total_bytes, + maxTotalBytes: snapshot2.max_total_bytes, + maxDurationMs: snapshot2.max_duration_ms + }, + deadlineMs: snapshot2.max_duration_ms + }); + if (!baseline.complete) { + const health = inspectNativeContentSnapshotHealth(baseline); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + throw new NativeBaselineIncompleteError( + state.name, + baseline.omittedCount, + omittedByReason, + health.samplePaths, + health.sampleTruncated, + baseline.limits, + baseline.policy?.hash ?? null + ); + } + await writeNativeBaselineManifest(options.paths, state.name, baseline); + await createNativeChangeFile(options.paths, state); + await writeNativeWorkspaceIdentity({ + paths: options.paths, + name: state.name, + revision: state.revision, + now: options.now + }); + return state; + } catch (error) { + if (createdChangeDir) await fs13.rm(changeDir, { recursive: true, force: true }); + throw error; + } +} +var NATIVE_CHANGE_DOCUMENT_MAX_BYTES = 256 * 1024; +async function readChangeDocumentFile(file, root = path16.dirname(file)) { + const ref = path16.relative(root, file).split(path16.sep).join("/"); + const source = await readNativeBoundedTextFile({ + root, + ref, + maxBytes: NATIVE_CHANGE_DOCUMENT_MAX_BYTES + }); + const document = (0, import_yaml3.parseDocument)(source.text, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid Native change file ${file}: ${document.errors[0].message}`); + } + return document.toJS(); +} +async function inspectNativeChange(paths, name) { + const file = path16.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + const inspection = inspectNativeChangeValue(await readChangeDocumentFile(file, paths.nativeRoot)); + if (inspection.state && inspection.state.name !== name) { + throw new Error(`Native change directory/name mismatch: ${name}`); + } + if (await hasPendingNativeSchemaMigration(paths, name)) { + return { + status: "migration-required", + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + state: inspection.state, + message: `Native schema migration is incomplete for ${name}; run doctor --repair` + }; + } + return inspection; +} +async function readNativeChange(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + throw new NativeSchemaMigrationRequiredError(name, inspection.schema); + } + if (inspection.status === "runtime-incompatible" || !inspection.state) { + throw new NativeRuntimeCompatibilityError(inspection.schema, inspection.minimumRuntimeVersion); + } + return inspection.state; +} +async function createNativeChangeFile(paths, state) { + const file = path16.join(nativeChangeDir(paths, state.name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs13.access(file); + throw new Error(`Native change state already exists: ${state.name}`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + if (state.revision !== 1) throw new Error("New Native change must start at revision 1"); + await atomicWriteText(file, (0, import_yaml3.stringify)(nativeChangeDocument(state))); +} +async function compareAndSwapNativeChangeFile(file, state, expectedRevision) { + const next = { + ...state, + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: expectedRevision + 1 + }; + const result2 = await compareAndSwapNativeRevision({ + expectedRevision, + next, + read: async () => { + const current = parseNativeChangeValue(await readChangeDocumentFile(file)); + if (current.name !== state.name) { + throw new Error(`Native change file/name mismatch: ${state.name}`); + } + return current; + }, + write: (value) => atomicWriteText(file, (0, import_yaml3.stringify)(nativeChangeDocument(value))), + equals: (left, right) => JSON.stringify(nativeChangeDocument(left)) === JSON.stringify(nativeChangeDocument(right)), + conflict: (actualRevision) => new NativeChangeRevisionConflictError(state.name, expectedRevision, actualRevision) + }); + Object.assign(state, result2); + return result2; +} +async function compareAndSwapNativeChangeLocked(paths, state, expectedRevision, options) { + await assertNoPendingNativeRootMove(paths.projectRoot); + if (await hasPendingNativeSchemaMigration(paths, state.name)) { + throw new NativeSchemaMigrationRequiredError(state.name, state.schema); + } + if (!options?.allowPendingCheckpointRecovery && await hasPendingNativeCheckpointRecovery(paths, state.name)) { + throw new Error( + `Native progress checkpoint recovery is required for ${state.name} before another state write` + ); + } + await assertNativeTrajectoryHealthy(paths, state.name); + const file = path16.join(nativeChangeDir(paths, state.name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + return compareAndSwapNativeChangeFile(file, state, expectedRevision); +} +async function writeNativeChangeFile(file, state) { + return compareAndSwapNativeChangeFile(file, state, state.revision); +} +async function readNativeChangeFile(file) { + return parseNativeChangeValue(await readChangeDocumentFile(file)); +} + +// domains/comet-native/native-artifacts.ts +var BRIEF_REQUIRED = ["Outcome", "Scope", "Non-goals", "Acceptance examples"]; +var BRIEF_ALL = [ + ...BRIEF_REQUIRED, + "Constraints and invariants", + "Decisions", + "Open questions", + "Verification expectations" +]; +var VERIFICATION_ALL = [ + "Acceptance evidence", + "Commands and results", + "Skipped checks", + "Spec consistency", + "Known limitations and risks", + "Conclusion" +]; +var NATIVE_ARTIFACT_VALIDATION_LIMITS = { + maxFileBytes: DEFAULT_NATIVE_ARTIFACT_MAX_BYTES +}; +function markdownSections(source) { + const sections = /* @__PURE__ */ new Map(); + let heading = null; + let body = []; + const flush = () => { + if (heading !== null) sections.set(heading, body.join("\n").trim()); + }; + for (const line of source.split(/\r?\n/u)) { + const match = /^# ([^#].*)$/u.exec(line); + if (match) { + flush(); + heading = match[1].trim(); + body = []; + } else if (heading !== null) { + body.push(line); + } + } + flush(); + return sections; +} +async function readContainedFile(root, relativeRef) { + const target = path17.resolve(root, ...relativeRef.split(/[\\/]/u)); + if (!isInsidePath(root, target)) + throw new Error(`Artifact escapes Native change: ${relativeRef}`); + const realRoot = await fs14.realpath(root); + const realTarget = await fs14.realpath(target); + if (!isInsidePath(realRoot, realTarget)) { + throw new Error(`Artifact symlink escapes Native change: ${relativeRef}`); + } + if (!(await fs14.stat(realTarget)).isFile()) + throw new Error(`Artifact is not a file: ${relativeRef}`); + return realTarget; +} +function result(findings) { + return { valid: findings.length === 0, findings }; +} +async function validateNativeBrief(changeDir, briefRef) { + const findings = []; + let source; + try { + source = (await readNativeBoundedTextFile({ + root: changeDir, + ref: briefRef, + maxBytes: NATIVE_ARTIFACT_VALIDATION_LIMITS.maxFileBytes + })).text; + } catch (error) { + return result([{ code: "brief-missing", message: error.message, path: briefRef }]); + } + const sections = markdownSections(source); + for (const heading of BRIEF_ALL) { + if (!sections.has(heading)) { + findings.push({ + code: "brief-section-missing", + message: `Missing brief section: ${heading}`, + path: briefRef + }); + } + } + for (const heading of BRIEF_REQUIRED) { + if ((sections.get(heading) ?? "").length === 0) { + findings.push({ + code: "brief-section-empty", + message: `Brief section is empty: ${heading}`, + path: briefRef + }); + } + } + const openQuestions = sections.get("Open questions") ?? ""; + if (/^\s*-\s*\[blocking\]/imu.test(openQuestions)) { + findings.push({ + code: "brief-blocking-question", + message: "Brief has a blocking open question", + path: briefRef + }); + } + return result(findings); +} +async function validateNativeVerification(changeDir, reportRef) { + const findings = []; + let source; + try { + source = (await readNativeBoundedTextFile({ + root: changeDir, + ref: reportRef, + maxBytes: NATIVE_ARTIFACT_VALIDATION_LIMITS.maxFileBytes + })).text; + } catch (error) { + return result([ + { code: "verification-missing", message: error.message, path: reportRef } + ]); + } + const sections = markdownSections(source); + for (const heading of VERIFICATION_ALL) { + if (!sections.has(heading)) { + findings.push({ + code: "verification-section-missing", + message: `Missing verification section: ${heading}`, + path: reportRef + }); + } else if ((sections.get(heading) ?? "").length === 0) { + findings.push({ + code: "verification-section-empty", + message: `Verification section is empty: ${heading}`, + path: reportRef + }); + } + } + return result(findings); +} +function canonicalSpecPath(paths, capability) { + return path17.join(paths.specsDir, capability, "spec.md"); +} +async function validateNativeSpecChanges(paths, state) { + const findings = []; + const changeDir = nativeChangeDir(paths, state.name); + for (const change of state.spec_changes) { + const canonical = canonicalSpecPath(paths, change.capability); + let canonicalHash2 = null; + try { + await resolveContainedNativePath(paths.nativeRoot, canonical); + canonicalHash2 = await sha256File(canonical); + } catch (error) { + if (error.code !== "ENOENT") { + findings.push({ + code: "spec-canonical-unsafe", + message: error.message, + path: canonical + }); + continue; + } + } + if (change.operation === "create" && canonicalHash2 !== null) { + findings.push({ + code: "spec-create-exists", + message: `Canonical spec already exists: ${change.capability}`, + path: canonical + }); + } + if (change.operation !== "create" && canonicalHash2 === null) { + findings.push({ + code: "spec-base-missing", + message: `Canonical spec is missing: ${change.capability}`, + path: canonical + }); + } + if (change.operation !== "create" && canonicalHash2 !== null && canonicalHash2 !== change.base_hash) { + findings.push({ + code: "spec-base-conflict", + message: `Canonical spec changed for ${change.capability}: expected ${change.base_hash}, actual ${canonicalHash2}`, + path: canonical + }); + } + if (change.source) { + try { + await readContainedFile(changeDir, change.source); + } catch (error) { + findings.push({ + code: "spec-source-invalid", + message: error.message, + path: change.source + }); + } + } + } + return result(findings); +} +async function resolveNativeArtifactFile(changeDir, relativeRef) { + return readContainedFile(changeDir, relativeRef); +} + +// domains/comet-native/native-archive-inspection.ts +import { promises as fs18 } from "node:fs"; +import path29 from "node:path"; + +// domains/comet-native/native-archive-preflight.ts +import path18 from "node:path"; + +// domains/comet-native/native-canonical-hash.ts +import { createHash as createHash9 } from "crypto"; +function invalidCanonicalJson(detail) { + throw new TypeError(`Value is not valid canonical JSON: ${detail}`); +} +function canonicalArray(value, ancestors) { + if (ancestors.has(value)) invalidCanonicalJson("cyclic structures are not supported"); + ancestors.add(value); + try { + const enumerableKeys = Object.keys(value); + for (let index = 0; index < value.length; index += 1) { + if (!Object.prototype.hasOwnProperty.call(value, index)) { + invalidCanonicalJson("sparse arrays are not supported"); + } + } + if (enumerableKeys.length !== value.length || enumerableKeys.some((key, index) => key !== String(index))) { + invalidCanonicalJson("arrays must not have named enumerable properties"); + } + if (Object.getOwnPropertySymbols(value).length > 0) { + invalidCanonicalJson("symbol properties are not supported"); + } + return `[${value.map((entry2) => canonicalValue(entry2, ancestors)).join(",")}]`; + } finally { + ancestors.delete(value); + } +} +function canonicalObject(value, ancestors) { + const prototype = Object.getPrototypeOf(value); + if (prototype !== Object.prototype && prototype !== null) { + invalidCanonicalJson("only plain objects are supported"); + } + if (ancestors.has(value)) invalidCanonicalJson("cyclic structures are not supported"); + if (Object.getOwnPropertySymbols(value).length > 0) { + invalidCanonicalJson("symbol properties are not supported"); + } + ancestors.add(value); + try { + const descriptors = Object.getOwnPropertyDescriptors(value); + const keys = Object.keys(value).sort(); + const fields = keys.map((key) => { + const descriptor = descriptors[key]; + if (!descriptor || !("value" in descriptor)) { + invalidCanonicalJson("accessor properties are not supported"); + } + return `${JSON.stringify(key)}:${canonicalValue(descriptor.value, ancestors)}`; + }); + return `{${fields.join(",")}}`; + } finally { + ancestors.delete(value); + } +} +function canonicalValue(value, ancestors) { + if (value === null) return "null"; + switch (typeof value) { + case "boolean": + return value ? "true" : "false"; + case "string": + return JSON.stringify(value); + case "number": + if (!Number.isFinite(value)) invalidCanonicalJson("numbers must be finite"); + return JSON.stringify(Object.is(value, -0) ? 0 : value); + case "object": + return Array.isArray(value) ? canonicalArray(value, ancestors) : canonicalObject(value, ancestors); + case "bigint": + case "function": + case "symbol": + case "undefined": + return invalidCanonicalJson(`${typeof value} values are not supported`); + } + return invalidCanonicalJson("unsupported value type"); +} +function canonicalJson(value) { + return canonicalValue(value, /* @__PURE__ */ new Set()); +} +function canonicalHash(tag, value) { + if (tag.length === 0) throw new TypeError("Canonical hash tag must be non-empty"); + if (/[\r\n]/u.test(tag)) { + throw new TypeError("Canonical hash tag must not contain a line break"); + } + return createHash9("sha256").update(`${tag} +${canonicalJson(value)}`).digest("hex"); +} + +// domains/comet-native/native-archive-preflight.ts +var NATIVE_ARCHIVE_PREFLIGHT_SCHEMA = "comet.native.archive-preflight.v1"; +var PREFLIGHT_HASH_TAG = "comet.native.archive-preflight.v1"; +var OPERATION_HASH_TAG = "comet.native.archive-operation.v1"; +var HASH_PATTERN4 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN2 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var FINDING_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +function compareText(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function hash(value, label) { + if (typeof value !== "string" || !HASH_PATTERN4.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function optionalHash(value, label) { + return value === null ? null : hash(value, label); +} +function normalizedRef(value, label) { + const normalized = path18.posix.normalize(value); + if (typeof value !== "string" || value.length === 0 || value !== value.trim() || value.includes("\\") || path18.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized Native-relative ref`); + } + return value; +} +function positiveRevision(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native archive preflight revision must be a positive integer"); + } + return value; +} +function normalizeSpec(value, index) { + if (!NAME_PATTERN2.test(value.capability)) { + throw new Error(`Native archive spec ${index} capability is invalid`); + } + if (value.operation !== "create" && value.operation !== "replace" && value.operation !== "remove") { + throw new Error(`Native archive spec ${value.capability} operation is invalid`); + } + const expectedBaseHash = optionalHash( + value.expectedBaseHash, + `Native archive spec ${value.capability} expected base` + ); + const actualBaseHash = optionalHash( + value.actualBaseHash, + `Native archive spec ${value.capability} actual base` + ); + const proposedHash = optionalHash( + value.proposedHash, + `Native archive spec ${value.capability} proposed content` + ); + if (value.operation === "create" && expectedBaseHash !== null) { + throw new Error(`Native archive create ${value.capability} must expect no canonical base`); + } + if (value.operation !== "create" && expectedBaseHash === null) { + throw new Error(`Native archive ${value.operation} ${value.capability} requires a base hash`); + } + if (value.operation === "remove" !== (proposedHash === null)) { + throw new Error( + `Native archive ${value.operation} ${value.capability} proposed hash is invalid` + ); + } + return { + capability: value.capability, + operation: value.operation, + expectedBaseHash, + actualBaseHash, + proposedHash + }; +} +function normalizeEvidence(value) { + if (value.result !== "pending" && value.result !== "pass" && value.result !== "fail") { + throw new Error("Native archive verification result is invalid"); + } + if (!["missing", "invalid", "stale", "complete", "partial"].includes(value.freshness)) { + throw new Error("Native archive verification freshness is invalid"); + } + if (!Number.isSafeInteger(value.skippedAcceptanceCount) || value.skippedAcceptanceCount < 0) { + throw new Error("Native archive skipped acceptance count is invalid"); + } + const normalized = { + result: value.result, + freshness: value.freshness, + contractHash: optionalHash(value.contractHash, "Native archive contract hash"), + acceptanceHash: optionalHash(value.acceptanceHash, "Native archive acceptance hash"), + implementationScopeHash: optionalHash( + value.implementationScopeHash, + "Native archive implementation scope hash" + ), + reportHash: optionalHash(value.reportHash, "Native archive report hash"), + envelopeHash: optionalHash(value.envelopeHash, "Native archive envelope hash"), + partialAllowanceHash: optionalHash( + value.partialAllowanceHash, + "Native archive partial allowance hash" + ), + skippedAcceptanceCount: value.skippedAcceptanceCount + }; + if ((value.freshness === "complete" || value.freshness === "partial") && [ + normalized.contractHash, + normalized.acceptanceHash, + normalized.implementationScopeHash, + normalized.reportHash, + normalized.envelopeHash + ].some((entry2) => entry2 === null)) { + throw new Error("Fresh Native archive evidence requires every bound content hash"); + } + if (value.freshness === "partial" && normalized.partialAllowanceHash === null || value.freshness === "complete" && normalized.partialAllowanceHash !== null) { + throw new Error("Native archive partial evidence allowance state is invalid"); + } + return normalized; +} +function normalizeFindingCodes(values) { + if (values.length > 64) throw new Error("Native archive preflight has too many findings"); + const codes = values.map((value) => { + if (typeof value !== "string" || !FINDING_PATTERN.test(value)) { + throw new Error(`Native archive preflight finding code is invalid: ${String(value)}`); + } + return value; + }); + const normalized = [...new Set(codes)].sort(compareText); + if (normalized.length !== codes.length) { + throw new Error("Native archive preflight has duplicate finding codes"); + } + return normalized; +} +function derivedFindings(input) { + const findings = []; + if (input.phase !== "archive") findings.push("archive-phase-required"); + if (input.archived) findings.push("change-already-archived"); + if (input.pendingJournal) findings.push("pending-journal"); + if (input.targetExists) findings.push("archive-target-exists"); + if (input.evidence.result !== "pass") findings.push("verification-not-passed"); + if (input.evidence.freshness === "missing") findings.push("verification-evidence-missing"); + if (input.evidence.freshness === "invalid") findings.push("verification-evidence-invalid"); + if (input.evidence.freshness === "stale") findings.push("verification-evidence-stale"); + for (const spec of input.specs) { + if (spec.actualBaseHash !== spec.expectedBaseHash) findings.push("spec-base-conflict"); + } + return [...new Set(findings)].sort(compareText); +} +function buildNativeArchivePreflight(input) { + if (!NAME_PATTERN2.test(input.change)) throw new Error("Native archive change name is invalid"); + if (typeof input.stateSchema !== "string" || input.stateSchema.length === 0) { + throw new Error("Native archive state schema is invalid"); + } + if (!Array.isArray(input.specs) || input.specs.length > 64) { + throw new Error("Native archive preflight exceeds its spec budget"); + } + const specs = input.specs.map(normalizeSpec).sort((left, right) => compareText(left.capability, right.capability)); + if (new Set(specs.map((spec) => spec.capability)).size !== specs.length) { + throw new Error("Native archive preflight contains duplicate capabilities"); + } + const operations = specs.map((spec) => ({ + ...spec, + operationHash: canonicalHash(OPERATION_HASH_TAG, spec) + })); + const evidence = normalizeEvidence(input.evidence); + const findingCodes = [ + .../* @__PURE__ */ new Set([ + ...normalizeFindingCodes(input.findingCodes ?? []), + ...derivedFindings({ + phase: input.phase, + archived: input.archived, + pendingJournal: input.pendingJournal, + targetExists: input.targetExists, + specs, + evidence + }) + ]) + ].sort(compareText); + const revision = positiveRevision(input.revision); + const targetRef = normalizedRef(input.targetRef, "Native archive target"); + const facts = { + stateSchema: input.stateSchema, + change: input.change, + revision, + phase: input.phase, + archived: input.archived, + pendingJournal: input.pendingJournal, + targetRef, + targetExists: input.targetExists, + operations, + evidence, + findingCodes + }; + return { + schema: NATIVE_ARCHIVE_PREFLIGHT_SCHEMA, + change: input.change, + revision, + targetRef, + ready: findingCodes.length === 0, + evidenceFreshness: evidence.freshness, + operationCount: operations.length, + operations, + findingCodes, + preflightHash: canonicalHash(PREFLIGHT_HASH_TAG, facts) + }; +} + +// domains/comet-native/native-conflict-radar.ts +import { createHash as createHash10 } from "node:crypto"; +import path19 from "node:path"; +var NATIVE_CONFLICT_RADAR_SCHEMA = "comet.native.conflict-radar.v1"; +var NATIVE_CONFLICT_RADAR_LIMITS = Object.freeze({ + maxChanges: 32, + maxSpecsPerChange: 64, + maxArtifactsPerChange: 128, + maxTotalSpecs: 512, + maxTotalArtifacts: 1024, + maxSignalsPerRelationship: 8, + maxRelationships: 256, + maxSerializedBytes: 64 * 1024, + maxNameBytes: 128, + maxCapabilityBytes: 128, + maxArtifactPathBytes: 512 +}); +var RADAR_HASH_TAG = "comet.native.conflict-radar.v1"; +var SIGNAL_SET_HASH_TAG = "comet.native.conflict-radar-signals.v1"; +var HASH_PATTERN5 = /^[a-f0-9]{64}$/u; +var NAME_PATTERN3 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CHANGE_KEYS2 = /* @__PURE__ */ new Set([ + "name", + "revision", + "specs", + "declaredArtifacts", + "workspaceIdentityHash" +]); +var SPEC_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "baseHash"]); +var ARTIFACT_KEYS = /* @__PURE__ */ new Set(["path", "kind"]); +function compareText2(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function byteLength(value) { + return Buffer.byteLength(value, "utf8"); +} +function strictRecord(value, keys, label) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + const record8 = value; + const unknown = Object.keys(record8).filter((key) => !keys.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + return record8; +} +function boundedText(value, label, maxBytes) { + if (typeof value !== "string" || value.length === 0 || value !== value.trim()) { + throw new Error(`${label} must be a non-empty trimmed string`); + } + if (byteLength(value) > maxBytes) throw new Error(`${label} exceeds its byte budget`); + return value; +} +function normalizedProjectPath(value, label) { + const candidate = boundedText(value, label, NATIVE_CONFLICT_RADAR_LIMITS.maxArtifactPathBytes); + const normalized = path19.posix.normalize(candidate); + if (candidate.includes("\\") || Array.from(candidate).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || /^(?:[A-Za-z]:|~)/u.test(candidate) || path19.posix.isAbsolute(normalized) || candidate.split("/").includes("..") || normalized !== candidate || normalized === "." || candidate.endsWith("/")) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return candidate; +} +function normalizedHash(value, label) { + if (typeof value !== "string" || !HASH_PATTERN5.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function normalizeSpec2(value, label) { + const record8 = strictRecord(value, SPEC_KEYS, label); + const capability = boundedText( + record8.capability, + `${label} capability`, + NATIVE_CONFLICT_RADAR_LIMITS.maxCapabilityBytes + ); + if (!NAME_PATTERN3.test(capability)) throw new Error(`${label} capability is invalid`); + if (record8.operation !== "create" && record8.operation !== "replace" && record8.operation !== "remove") { + throw new Error(`${label} operation is invalid`); + } + const baseHash = record8.baseHash === null ? null : normalizedHash(record8.baseHash, `${label} canonical base hash`); + if (record8.operation === "create" && baseHash !== null) { + throw new Error(`${label} create operation requires a null canonical base hash`); + } + if (record8.operation !== "create" && baseHash === null) { + throw new Error(`${label} ${record8.operation} operation requires a canonical base hash`); + } + return { capability, operation: record8.operation, baseHash }; +} +function normalizeArtifact(value, label) { + const record8 = strictRecord(value, ARTIFACT_KEYS, label); + if (record8.kind !== "file" && record8.kind !== "directory") { + throw new Error(`${label} kind is invalid`); + } + return { path: normalizedProjectPath(record8.path, `${label} path`), kind: record8.kind }; +} +function normalizeChange(value, index) { + const record8 = strictRecord(value, CHANGE_KEYS2, `Conflict radar change ${index}`); + const name = boundedText( + record8.name, + `Conflict radar change ${index} name`, + NATIVE_CONFLICT_RADAR_LIMITS.maxNameBytes + ); + if (!NAME_PATTERN3.test(name)) throw new Error(`Conflict radar change ${index} name is invalid`); + if (!Number.isSafeInteger(record8.revision) || record8.revision < 1) { + throw new Error(`Conflict radar change ${name} revision must be a positive safe integer`); + } + if (!Array.isArray(record8.specs)) { + throw new Error(`Conflict radar change ${name} specs must be an array`); + } + if (record8.specs.length > NATIVE_CONFLICT_RADAR_LIMITS.maxSpecsPerChange) { + throw new Error(`Conflict radar change ${name} exceeds the spec budget`); + } + if (!Array.isArray(record8.declaredArtifacts)) { + throw new Error(`Conflict radar change ${name} declared artifacts must be an array`); + } + if (record8.declaredArtifacts.length > NATIVE_CONFLICT_RADAR_LIMITS.maxArtifactsPerChange) { + throw new Error(`Conflict radar change ${name} exceeds the declared artifact budget`); + } + const specs = record8.specs.map( + (spec, specIndex) => normalizeSpec2(spec, `Conflict radar change ${name} spec ${specIndex}`) + ).sort((left, right) => compareText2(left.capability, right.capability)); + if (new Set(specs.map((spec) => spec.capability)).size !== specs.length) { + throw new Error(`Conflict radar change ${name} has duplicate capabilities`); + } + const declaredArtifacts = record8.declaredArtifacts.map( + (artifact, artifactIndex) => normalizeArtifact(artifact, `Conflict radar change ${name} artifact ${artifactIndex}`) + ).sort( + (left, right) => compareText2(left.path, right.path) || compareText2(left.kind, right.kind) + ); + const artifactPaths = declaredArtifacts.map((artifact) => artifact.path); + if (new Set(artifactPaths).size !== artifactPaths.length) { + throw new Error(`Conflict radar change ${name} has duplicate or conflicting artifact paths`); + } + const workspaceIdentityHash = record8.workspaceIdentityHash === void 0 || record8.workspaceIdentityHash === null ? null : normalizedHash( + record8.workspaceIdentityHash, + `Conflict radar change ${name} workspace identity hash` + ); + return { + name, + revision: record8.revision, + specs, + declaredArtifacts, + workspaceIdentityHash + }; +} +function normalizeChanges(input) { + if (!Array.isArray(input)) throw new Error("Native conflict radar input must be an array"); + if (input.length > NATIVE_CONFLICT_RADAR_LIMITS.maxChanges) { + throw new Error("Native conflict radar exceeds the change budget"); + } + const changes = input.map(normalizeChange).sort((left, right) => compareText2(left.name, right.name)); + if (new Set(changes.map((change) => change.name)).size !== changes.length) { + throw new Error("Native conflict radar contains duplicate change names"); + } + const specCount = changes.reduce((total, change) => total + change.specs.length, 0); + if (specCount > NATIVE_CONFLICT_RADAR_LIMITS.maxTotalSpecs) { + throw new Error("Native conflict radar exceeds the total spec budget"); + } + const artifactCount = changes.reduce( + (total, change) => total + change.declaredArtifacts.length, + 0 + ); + if (artifactCount > NATIVE_CONFLICT_RADAR_LIMITS.maxTotalArtifacts) { + throw new Error("Native conflict radar exceeds the total declared artifact budget"); + } + return changes; +} +function capabilitySignal(left, right) { + const sameCanonicalBase = left.baseHash !== null && left.baseHash === right.baseHash; + const certainty = left.operation === "create" || right.operation === "create" || sameCanonicalBase ? "definite-conflict" : "possible-overlap"; + return { + kind: "capability", + certainty, + capability: left.capability, + leftOperation: left.operation, + rightOperation: right.operation, + leftBaseHash: left.baseHash, + rightBaseHash: right.baseHash + }; +} +function artifactOverlap(left, right) { + const leftOwnsRight = left.kind === "directory" && (right.path === left.path || right.path.startsWith(`${left.path}/`)); + const rightOwnsLeft = right.kind === "directory" && (left.path === right.path || left.path.startsWith(`${right.path}/`)); + const sameFile = left.kind === "file" && right.kind === "file" && left.path === right.path; + if (!leftOwnsRight && !rightOwnsLeft && !sameFile) return null; + return { + kind: "artifact", + certainty: sameFile ? "definite-conflict" : "possible-overlap", + leftArtifact: left, + rightArtifact: right + }; +} +function workspaceRelationship(left, right) { + if (left.workspaceIdentityHash === null || right.workspaceIdentityHash === null) return "unknown"; + return left.workspaceIdentityHash === right.workspaceIdentityHash ? "same" : "different"; +} +function relationship(left, right) { + const visibleSignals = []; + const signalHasher = createHash10("sha256").update(`${SIGNAL_SET_HASH_TAG} +[`); + let signalCount = 0; + let hasDefiniteConflict = false; + const addSignal = (signal) => { + if (signalCount > 0) signalHasher.update(","); + signalHasher.update(canonicalJson(signal)); + signalCount += 1; + if (signal.certainty === "definite-conflict") hasDefiniteConflict = true; + if (visibleSignals.length < NATIVE_CONFLICT_RADAR_LIMITS.maxSignalsPerRelationship) { + visibleSignals.push(signal); + } + }; + const rightSpecs = new Map(right.specs.map((spec) => [spec.capability, spec])); + for (const leftSpec of left.specs) { + const rightSpec = rightSpecs.get(leftSpec.capability); + if (rightSpec) addSignal(capabilitySignal(leftSpec, rightSpec)); + } + for (const leftArtifact of left.declaredArtifacts) { + for (const rightArtifact of right.declaredArtifacts) { + const signal = artifactOverlap(leftArtifact, rightArtifact); + if (signal) addSignal(signal); + } + } + const classification = hasDefiniteConflict ? "definite-conflict" : signalCount > 0 ? "possible-overlap" : "disjoint"; + signalHasher.update("]"); + return { + left: left.name, + right: right.name, + classification, + workspaceRelationship: workspaceRelationship(left, right), + signalCount, + signalHash: signalHasher.digest("hex"), + signals: visibleSignals, + signalsTruncated: visibleSignals.length !== signalCount + }; +} +function classificationRank(value) { + if (value === "definite-conflict") return 0; + if (value === "possible-overlap") return 1; + return 2; +} +function compareRelationships(left, right) { + return classificationRank(left.classification) - classificationRank(right.classification) || compareText2(left.left, right.left) || compareText2(left.right, right.right); +} +function snapshot(changes, allRelationships, relationships, radarHash) { + const counts = { + definiteConflict: allRelationships.filter( + ({ classification }) => classification === "definite-conflict" + ).length, + possibleOverlap: allRelationships.filter( + ({ classification }) => classification === "possible-overlap" + ).length, + disjoint: allRelationships.filter(({ classification }) => classification === "disjoint").length + }; + return { + schema: NATIVE_CONFLICT_RADAR_SCHEMA, + workspaceIdentityAdvisoryOnly: true, + changeCount: changes.length, + relationshipCount: allRelationships.length, + counts, + relationships, + relationshipsTruncated: relationships.length !== allRelationships.length, + omittedRelationshipCount: allRelationships.length - relationships.length, + radarHash + }; +} +function buildNativeConflictRadar(input) { + const changes = normalizeChanges(input); + const allRelationships = []; + for (let leftIndex = 0; leftIndex < changes.length; leftIndex += 1) { + for (let rightIndex = leftIndex + 1; rightIndex < changes.length; rightIndex += 1) { + allRelationships.push(relationship(changes[leftIndex], changes[rightIndex])); + } + } + allRelationships.sort(compareRelationships); + const hashRelationships = allRelationships.map((value) => ({ + left: value.left, + right: value.right, + classification: value.classification, + workspaceRelationship: value.workspaceRelationship, + signalCount: value.signalCount, + signalHash: value.signalHash + })); + const radarHash = canonicalHash(RADAR_HASH_TAG, { changes, relationships: hashRelationships }); + let relationships = allRelationships.slice(0, NATIVE_CONFLICT_RADAR_LIMITS.maxRelationships).map((value) => ({ ...value, signals: [...value.signals] })); + let result2 = snapshot(changes, allRelationships, relationships, radarHash); + while (relationships.length > 0 && byteLength(JSON.stringify(result2)) > NATIVE_CONFLICT_RADAR_LIMITS.maxSerializedBytes) { + relationships = relationships.slice(0, -1); + result2 = snapshot(changes, allRelationships, relationships, radarHash); + } + if (byteLength(JSON.stringify(result2)) > NATIVE_CONFLICT_RADAR_LIMITS.maxSerializedBytes) { + throw new Error("Native conflict radar summary exceeds its serialized output budget"); + } + return result2; +} + +// domains/comet-native/native-evidence-storage.ts +import { createHash as createHash12 } from "node:crypto"; +import { promises as fs15 } from "node:fs"; +import path22 from "node:path"; + +// domains/comet-native/native-verification-scope.ts +import { createHash as createHash11 } from "node:crypto"; +import path20 from "path"; +var NATIVE_IMPLEMENTATION_SCOPE_SCHEMA = "comet.native.implementation-scope.v2"; +var NATIVE_SNAPSHOT_PROJECTION_SCHEMA = "comet.native.content-snapshot-projection.v1"; +var SNAPSHOT_PROJECTION_HASH_TAG = "comet.native.content-snapshot-projection.v1"; +var IMPLEMENTATION_SCOPE_HASH_TAG = "comet.native.implementation-scope.v2"; +var UNRESOLVED_SCOPE_ID_TAG = "comet.native.unresolved-scope-id.v1"; +var SCOPE_DETAIL_OVERFLOW_HASH_TAG = "comet.native.scope-detail-overflow.v1"; +var SNAPSHOT_PROJECTION_OVERFLOW_HASH_TAG = "comet.native.snapshot-projection-overflow.v2"; +var SHA256_HASH_PATTERN = /^[a-f0-9]{64}$/u; +var MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES = 1024 * 1024; +var MAX_NATIVE_DETAILED_SCOPE_CHANGES = 128; +var MAX_NATIVE_DETAILED_UNRESOLVED_SCOPES = 128; +function compareText3(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function nativeSnapshotProjectionRef(hash6) { + if (!SHA256_HASH_PATTERN.test(hash6)) { + throw new Error("Native snapshot projection hash must be a SHA-256 hash"); + } + return `runtime/evidence/snapshots/${hash6}.json`; +} +function compareNullableText(left, right) { + if (left === right) return 0; + if (left === null) return -1; + if (right === null) return 1; + return compareText3(left, right); +} +function projectRelativePath(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.endsWith("/") || /^[a-zA-Z]:/u.test(value)) { + throw new Error(`${label} must be a normalized project-relative path`); + } + const normalized = path20.posix.normalize(value); + if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../") || path20.posix.isAbsolute(normalized)) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function snapshotOmissionPath(value, label) { + return value === "." ? value : projectRelativePath(value, label); +} +function nonNegativeSafeInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative safe integer`); + } + return value; +} +function normalizeEntry(entry2, label) { + if (entry2.type !== "file") throw new Error(`${label} must describe a file`); + if (typeof entry2.hash !== "string" || entry2.hash.length === 0) { + throw new Error(`${label} hash must be non-empty`); + } + return { + path: projectRelativePath(entry2.path, `${label} path`), + hash: entry2.hash, + size: nonNegativeSafeInteger(entry2.size, `${label} size`), + type: "file" + }; +} +function normalizeOmission(omission, label) { + return { + path: snapshotOmissionPath(omission.path, `${label} path`), + size: omission.size === null ? null : nonNegativeSafeInteger(omission.size, `${label} size`), + type: omission.type, + reason: omission.reason + }; +} +function compareEntries(left, right) { + return compareText3(left.path, right.path) || compareText3(left.hash, right.hash) || left.size - right.size; +} +function compareOmissions(left, right) { + return compareText3(left.path, right.path) || compareText3(left.reason, right.reason) || compareText3(left.type, right.type) || (left.size ?? -1) - (right.size ?? -1); +} +function serializedEvidenceBytes(value) { + return Buffer.byteLength(JSON.stringify(value, null, 2) + "\n", "utf8"); +} +function updateFramedHash(hash6, kind, value) { + const encoded = `${kind} +${canonicalJson(value)}`; + hash6.update(`${Buffer.byteLength(encoded, "utf8")}:`); + hash6.update(encoded); +} +function fitSnapshotProjectionBudget(value) { + if (serializedEvidenceBytes(value) <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + return value; + } + const entries = [...value.entries]; + const omitted = [...value.omitted]; + let omittedCount = value.omittedCount; + let overflowCount = value.omissionOverflow?.count ?? 0; + const overflowHash = createHash11("sha256"); + overflowHash.update(`${SNAPSHOT_PROJECTION_OVERFLOW_HASH_TAG} +`); + if (value.omissionOverflow) { + updateFramedHash(overflowHash, "existing-overflow", value.omissionOverflow); + } + const foldOmission = (omission, alreadyCounted) => { + if (!alreadyCounted) omittedCount += 1; + overflowCount += 1; + updateFramedHash(overflowHash, "omission", omission); + }; + const foldEntry = (entry2) => { + omittedCount += 1; + overflowCount += 1; + updateFramedHash(overflowHash, "entry", entry2); + }; + const takeCompactableOmission = () => { + for (let index = omitted.length - 1; index >= 0; index -= 1) { + const omission = omitted[index]; + if (omission.reason === "git-enumeration-limit" || omission.reason === "git-selection-changed" || omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed") { + continue; + } + omitted.splice(index, 1); + return omission; + } + return null; + }; + const candidate = () => ({ + ...value, + complete: omittedCount === 0, + entries, + omitted, + omittedCount, + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${"0".repeat(64)}`, + hash: "0".repeat(64), + count: overflowCount + } + }); + let projection = candidate(); + while (serializedEvidenceBytes(projection) > MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + const compactableOmissionCount = omitted.filter( + (omission) => omission.reason !== "git-enumeration-limit" && omission.reason !== "git-selection-changed" && omission.reason !== "physical-enumeration-limit" && omission.reason !== "physical-selection-changed" + ).length; + if (compactableOmissionCount > 0) { + const removeCount = Math.max(1, Math.ceil(omitted.length / 4)); + for (let removed = 0; removed < removeCount; removed += 1) { + const omission = takeCompactableOmission(); + if (omission === null) break; + foldOmission(omission, true); + } + } else if (entries.length > 0) { + const removeCount = Math.max(1, Math.ceil(entries.length / 4)); + for (const entry2 of entries.splice(-removeCount)) { + foldEntry(entry2); + } + } else { + throw new Error("Native snapshot projection metadata exceeds its evidence byte budget"); + } + projection = candidate(); + } + const digest = overflowHash.digest("hex"); + return { + ...projection, + omissionOverflow: { + ref: `native-snapshot://omitted-overflow/${digest}`, + hash: digest, + count: overflowCount + } + }; +} +function snapshotProjection(manifest) { + const parsed = parseNativeContentSnapshotManifest(manifest); + const entries = parsed.entries.map( + (entry2, index) => normalizeEntry(entry2, `Snapshot entry ${index}`) + ); + entries.sort(compareEntries); + if (new Set(entries.map((entry2) => entry2.path)).size !== entries.length) { + throw new Error("Snapshot entries must not contain duplicate paths"); + } + const omitted = parsed.omitted.map( + (omission, index) => normalizeOmission(omission, `Snapshot omission ${index}`) + ); + omitted.sort(compareOmissions); + return fitSnapshotProjectionBudget({ + schema: NATIVE_SNAPSHOT_PROJECTION_SCHEMA, + origin: parsed.origin, + ...parsed.capture ? { capture: parsed.capture } : {}, + complete: parsed.complete, + limits: { + maxFiles: parsed.limits.maxFiles, + maxFileBytes: parsed.limits.maxFileBytes, + maxTotalBytes: parsed.limits.maxTotalBytes, + maxManifestBytes: parsed.limits.maxManifestBytes, + ...parsed.limits.maxDurationMs === void 0 ? {} : { maxDurationMs: parsed.limits.maxDurationMs } + }, + ...parsed.policy ? { policy: parsed.policy } : {}, + entries, + omitted, + omittedCount: parsed.omittedCount, + ...parsed.omissionOverflow ? { + omissionOverflow: { + ref: parsed.omissionOverflow.ref, + hash: parsed.omissionOverflow.hash, + count: parsed.omissionOverflow.count + } + } : {} + }); +} +function normalizeDeclaredArtifacts(artifacts) { + const byPath = /* @__PURE__ */ new Map(); + for (const [index, artifact] of artifacts.entries()) { + if (artifact.kind !== "file" && artifact.kind !== "directory") { + throw new Error(`Declared artifact ${index} kind is invalid`); + } + const normalized = { + path: projectRelativePath(artifact.path, `Declared artifact ${index} path`), + kind: artifact.kind + }; + const existing = byPath.get(normalized.path); + if (existing && existing.kind !== normalized.kind) { + throw new Error(`Declared artifact path has conflicting kinds: ${normalized.path}`); + } + byPath.set(normalized.path, normalized); + } + return [...byPath.values()].sort( + (left, right) => compareText3(left.path, right.path) || compareText3(left.kind, right.kind) + ); +} +function artifactOwnsPath(artifact, changedPath) { + if (artifact.kind === "file") return artifact.path === changedPath; + return changedPath === artifact.path || changedPath.startsWith(`${artifact.path}/`); +} +function fileIdentity(entry2) { + return entry2 ? { hash: entry2.hash, size: entry2.size } : null; +} +function visitDerivedChanges(baseline, current, visitor) { + let beforeIndex = 0; + let afterIndex = 0; + while (beforeIndex < baseline.entries.length || afterIndex < current.entries.length) { + const beforeCandidate = baseline.entries[beforeIndex]; + const afterCandidate = current.entries[afterIndex]; + const order = beforeCandidate === void 0 ? 1 : afterCandidate === void 0 ? -1 : compareText3(beforeCandidate.path, afterCandidate.path); + const before = order <= 0 ? beforeCandidate : void 0; + const after = order >= 0 ? afterCandidate : void 0; + if (order <= 0) beforeIndex += 1; + if (order >= 0) afterIndex += 1; + if (before && after && before.hash === after.hash && before.size === after.size) continue; + if (before && !after && !current.complete) continue; + const changedPath = before?.path ?? after?.path; + if (changedPath === void 0) continue; + visitor({ + path: changedPath, + kind: before ? after ? "modified" : "removed" : "added", + before: fileIdentity(before), + after: fileIdentity(after) + }); + } +} +function materializeChange(core, declaredArtifacts) { + return { + ...core, + attributedTo: declaredArtifacts.filter((artifact) => artifactOwnsPath(artifact, core.path)) + }; +} +function unresolvedScope(identity, reason) { + return { + id: `scope:${canonicalHash(UNRESOLVED_SCOPE_ID_TAG, identity)}`, + kind: identity.kind, + source: identity.source, + path: identity.path, + reason + }; +} +function omissionScopes(source, projection) { + const scopes = projection.omitted.map( + (omission) => unresolvedScope( + { + kind: "snapshot-omission", + source, + path: omission.path, + evidence: { + reason: omission.reason, + size: omission.size, + type: omission.type + } + }, + `${source} snapshot omitted ${omission.path}: ${omission.reason}` + ) + ); + if (!projection.complete) { + scopes.push( + unresolvedScope( + { + kind: "snapshot-incomplete", + source, + path: null, + evidence: { omittedCount: projection.omittedCount } + }, + `${source} snapshot is incomplete` + ) + ); + } + if (projection.omissionOverflow) { + scopes.push( + unresolvedScope( + { + kind: "snapshot-omission-overflow", + source, + path: null, + evidence: { + count: projection.omissionOverflow.count, + hash: projection.omissionOverflow.hash, + ref: projection.omissionOverflow.ref + } + }, + `${source} snapshot has ${projection.omissionOverflow.count} unlisted omissions` + ) + ); + } + return scopes; +} +function omissionSummaryScopes(source, projection) { + return omissionScopes(source, { ...projection, omitted: [] }).filter( + (scope) => scope.kind !== "snapshot-omission" + ); +} +function compareUnresolvedScopes(left, right) { + return compareText3(left.kind, right.kind) || compareText3(left.source, right.source) || compareNullableText(left.path, right.path) || compareText3(left.id, right.id); +} +function uniqueUnresolvedScopes(scopes) { + const byId = new Map(scopes.map((scope) => [scope.id, scope])); + return [...byId.values()].sort(compareUnresolvedScopes); +} +function normalizeGitChangedPaths(paths) { + return [ + ...new Set(paths.map((value, index) => projectRelativePath(value, `Git path ${index}`))) + ].sort(compareText3); +} +function normalizeScopeAuthority(input) { + if (typeof input.contractHash !== "string" || !SHA256_HASH_PATTERN.test(input.contractHash)) { + throw new Error("Contract hash must be a SHA-256 hash"); + } + return { + contractHash: input.contractHash, + declaredArtifacts: normalizeDeclaredArtifacts(input.declaredArtifacts), + noCodeReason: input.noCodeReason?.trim() || null, + ...input.gitChangedPaths === void 0 ? {} : { gitChangedPaths: normalizeGitChangedPaths(input.gitChangedPaths) } + }; +} +function scanScopeChanges(baseline, current, declaredArtifacts, gitChangedPaths) { + const candidates = []; + const gitChangedPathSet = gitChangedPaths === void 0 ? null : new Set(gitChangedPaths); + const gitPathsPresentInChanges = []; + let candidateBytes = 0; + let candidateBudgetExhausted = false; + let totalChanges = 0; + let totalUnattributed = 0; + visitDerivedChanges(baseline, current, (core) => { + const attributed = declaredArtifacts.some((artifact) => artifactOwnsPath(artifact, core.path)); + totalChanges += 1; + if (!attributed) totalUnattributed += 1; + if (gitChangedPathSet?.has(core.path)) gitPathsPresentInChanges.push(core.path); + if (!candidateBudgetExhausted && candidates.length < MAX_NATIVE_DETAILED_SCOPE_CHANGES) { + const candidate = materializeChange(core, declaredArtifacts); + const nextBytes = serializedEvidenceBytes(candidate); + if (candidateBytes + nextBytes <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + candidates.push(candidate); + candidateBytes += nextBytes; + } else { + candidateBudgetExhausted = true; + } + } + }); + return { candidates, gitPathsPresentInChanges, totalChanges, totalUnattributed }; +} +function scopeDetailOverflow(hash6, counts) { + return unresolvedScope( + { + kind: "scope-detail-overflow", + source: "implementation-scope", + path: null, + evidence: { + changeCount: counts.changes, + hash: hash6, + unattributedCount: counts.unattributed, + unresolvedCount: counts.unresolved + } + }, + `Implementation scope summarized ${counts.changes} additional change details and ${counts.unresolved} unresolved details (${counts.unattributed} unattributed); overflow hash ${hash6}` + ); +} +function parseScopeDetailOverflow(scope) { + const match = /^Implementation scope summarized ([0-9]+) additional change details and ([0-9]+) unresolved details \(([0-9]+) unattributed\); overflow hash ([a-f0-9]{64})$/u.exec( + scope.reason + ); + if (!match) throw new Error("Implementation scope detail overflow is invalid"); + const counts = { + changes: nonNegativeSafeInteger(Number(match[1]), "Scope overflow change count"), + unresolved: nonNegativeSafeInteger(Number(match[2]), "Scope overflow unresolved count"), + unattributed: nonNegativeSafeInteger(Number(match[3]), "Scope overflow unattributed count") + }; + const hash6 = scopeHashValue(match[4], "Scope detail overflow hash"); + if (JSON.stringify(scope) !== JSON.stringify(scopeDetailOverflow(hash6, counts))) { + throw new Error("Implementation scope detail overflow is inconsistent"); + } + return { ...counts, hash: hash6 }; +} +function hashScopeDetailOverflow(options) { + const hash6 = createHash11("sha256"); + hash6.update(`${SCOPE_DETAIL_OVERFLOW_HASH_TAG} +`); + let changeIndex = 0; + visitDerivedChanges(options.baseline, options.current, (core) => { + if (changeIndex >= options.detailedChangeCount) { + updateFramedHash(hash6, "change", core); + let attributionCount = 0; + for (const artifact of options.declaredArtifacts) { + if (!artifactOwnsPath(artifact, core.path)) continue; + attributionCount += 1; + updateFramedHash(hash6, "change-attribution", artifact); + } + updateFramedHash(hash6, "change-end", { attributionCount }); + if (attributionCount === 0) { + updateFramedHash(hash6, "unattributed-change", { + after: core.after, + before: core.before, + changeKind: core.kind, + path: core.path + }); + } + } + changeIndex += 1; + }); + let omissionIndex = 0; + for (const [source, projection] of [ + ["baseline", options.baseline], + ["current", options.current] + ]) { + for (const omission of projection.omitted) { + if (omissionIndex >= options.detailedOmissionCount) { + updateFramedHash(hash6, "snapshot-omission", { source, ...omission }); + } + omissionIndex += 1; + } + } + return hash6.digest("hex"); +} +function buildScopeFromProjections(baseline, current, authority) { + const { contractHash, declaredArtifacts, noCodeReason } = authority; + const changeScan = scanScopeChanges( + baseline, + current, + declaredArtifacts, + authority.gitChangedPaths + ); + const baselineProjectionHash = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, baseline); + const currentProjectionHash2 = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, current); + const omissionCandidates = []; + for (const [source, projection] of [ + ["baseline", baseline], + ["current", current] + ]) { + for (const omission of projection.omitted) { + if (omissionCandidates.length >= MAX_NATIVE_DETAILED_UNRESOLVED_SCOPES) break; + omissionCandidates.push( + unresolvedScope( + { + kind: "snapshot-omission", + source, + path: omission.path, + evidence: { + reason: omission.reason, + size: omission.size, + type: omission.type + } + }, + `${source} snapshot omitted ${omission.path}: ${omission.reason}` + ) + ); + } + } + const totalOmissionDetails = baseline.omitted.length + current.omitted.length; + const essentialScopes = [ + ...omissionSummaryScopes("baseline", baseline), + ...omissionSummaryScopes("current", current), + ...changeScan.totalChanges === 0 && noCodeReason === null ? [ + unresolvedScope( + { + kind: "missing-no-code-reason", + source: "implementation-scope", + path: null, + evidence: { + baselineProjectionHash, + currentProjectionHash: currentProjectionHash2 + } + }, + "A non-empty no-code reason is required when the snapshots contain no changes" + ) + ] : [] + ]; + const buildGitAdvisory = () => { + if (authority.gitChangedPaths === void 0) return void 0; + const snapshotChangePaths = new Set(changeScan.gitPathsPresentInChanges); + return { + advisoryOnly: true, + changedPaths: authority.gitChangedPaths, + pathsPresentInSnapshotChanges: authority.gitChangedPaths.filter( + (value) => snapshotChangePaths.has(value) + ), + pathsAbsentFromSnapshotChanges: authority.gitChangedPaths.filter( + (value) => !snapshotChangePaths.has(value) + ) + }; + }; + const buildScopeContent = (options) => { + const changes = changeScan.candidates.slice(0, options.detailedChangeCount); + const unattributed = changes.filter((change) => change.attributedTo.length === 0); + const overflowChanges = changeScan.totalChanges - changes.length; + const overflowUnattributed = changeScan.totalUnattributed - unattributed.length; + const overflowOmissions = totalOmissionDetails - options.detailedOmissionCount; + const overflowUnresolved = overflowUnattributed + overflowOmissions; + const unresolved = [ + ...unattributed.map( + (change) => unresolvedScope( + { + kind: "unattributed-change", + source: "implementation-scope", + path: change.path, + evidence: { + after: change.after, + before: change.before, + changeKind: change.kind + } + }, + `Changed path is not covered by a declared artifact: ${change.path}` + ) + ), + ...omissionCandidates.slice(0, options.detailedOmissionCount), + ...essentialScopes, + ...overflowChanges > 0 || overflowUnresolved > 0 ? [ + scopeDetailOverflow(options.overflowHash, { + changes: overflowChanges, + unattributed: overflowUnattributed, + unresolved: overflowUnresolved + }) + ] : [] + ]; + const unresolvedScopes = uniqueUnresolvedScopes(unresolved); + const gitAdvisory = options.includeGitAdvisory ? buildGitAdvisory() : void 0; + return { + schema: NATIVE_IMPLEMENTATION_SCOPE_SCHEMA, + contractHash, + baselineProjectionRef: nativeSnapshotProjectionRef(baselineProjectionHash), + baselineProjectionHash, + currentProjectionRef: nativeSnapshotProjectionRef(currentProjectionHash2), + currentProjectionHash: currentProjectionHash2, + complete: unresolvedScopes.length === 0, + declaredArtifacts, + changes, + unattributed, + unresolvedScopes, + noCodeReason, + ...gitAdvisory ? { gitAdvisory } : {} + }; + }; + let detailedChangeCount = changeScan.candidates.length; + let detailedOmissionCount = omissionCandidates.length; + let includeGitAdvisory = authority.gitChangedPaths !== void 0; + const placeholderHash = "0".repeat(64); + while (true) { + const candidate = buildScopeContent({ + detailedChangeCount, + detailedOmissionCount, + includeGitAdvisory, + overflowHash: placeholderHash + }); + if (serializedEvidenceBytes({ ...candidate, scopeHash: placeholderHash }) <= MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + break; + } + if (includeGitAdvisory) { + includeGitAdvisory = false; + continue; + } + if (detailedOmissionCount > 0) { + detailedOmissionCount -= Math.max(1, Math.ceil(detailedOmissionCount / 4)); + continue; + } + if (detailedChangeCount > 0) { + const removable = detailedChangeCount; + detailedChangeCount -= Math.max(1, Math.ceil(removable / 4)); + continue; + } + throw new Error("Native implementation scope metadata exceeds its evidence byte budget"); + } + const hasOverflow = changeScan.totalChanges > detailedChangeCount || totalOmissionDetails > detailedOmissionCount; + const overflowHash = hasOverflow ? hashScopeDetailOverflow({ + baseline, + current, + declaredArtifacts, + detailedChangeCount, + detailedOmissionCount + }) : placeholderHash; + const scopeContent = buildScopeContent({ + detailedChangeCount, + detailedOmissionCount, + includeGitAdvisory, + overflowHash + }); + const scope = { + ...scopeContent, + scopeHash: canonicalHash(IMPLEMENTATION_SCOPE_HASH_TAG, scopeContent) + }; + if (serializedEvidenceBytes(scope) > MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES) { + throw new Error("Native implementation scope exceeded its evidence byte budget after fitting"); + } + return scope; +} +function buildNativeImplementationScopeBundle(input) { + const baseline = snapshotProjection(input.baseline); + const current = snapshotProjection(input.current); + const authority = normalizeScopeAuthority(input); + return { + authority, + baseline, + current, + scope: buildScopeFromProjections(baseline, current, authority) + }; +} +var SCOPE_KEYS = /* @__PURE__ */ new Set([ + "schema", + "contractHash", + "baselineProjectionRef", + "baselineProjectionHash", + "currentProjectionRef", + "currentProjectionHash", + "complete", + "declaredArtifacts", + "changes", + "unattributed", + "unresolvedScopes", + "noCodeReason", + "gitAdvisory", + "scopeHash" +]); +function scopeRecord(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactScopeKeys(value, required, optional, label) { + const allowed = /* @__PURE__ */ new Set([...required, ...optional]); + const unknown = Object.keys(value).filter((key) => !allowed.has(key)); + const missing = required.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function scopeHashValue(value, label) { + if (typeof value !== "string" || !SHA256_HASH_PATTERN.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function snapshotProjectionRefValue(value, hash6, label) { + const expected = nativeSnapshotProjectionRef(hash6); + if (value !== expected) throw new Error(`${label} ref/hash mismatch`); + return expected; +} +function parseNativeSnapshotProjection(value, expectedHash) { + const root = scopeRecord(value, "Native snapshot projection"); + exactScopeKeys( + root, + ["schema", "origin", "complete", "limits", "entries", "omitted", "omittedCount"], + ["capture", "omissionOverflow", "policy"], + "Native snapshot projection" + ); + if (root.schema !== NATIVE_SNAPSHOT_PROJECTION_SCHEMA) { + throw new Error("Native snapshot projection schema is invalid"); + } + const parsedManifest = parseNativeContentSnapshotManifest({ + schema: "comet.native.content-snapshot.v1", + origin: root.origin, + ...root.capture === void 0 ? {} : { capture: root.capture }, + createdAt: "1970-01-01T00:00:00.000Z", + complete: root.complete, + limits: root.limits, + ...root.policy === void 0 ? {} : { policy: root.policy }, + entries: root.entries, + omitted: root.omitted, + omittedCount: root.omittedCount, + ...root.omissionOverflow === void 0 ? {} : { omissionOverflow: root.omissionOverflow } + }); + const projection = snapshotProjection(parsedManifest); + if (canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, root) !== canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, projection)) { + throw new Error("Native snapshot projection is not canonical"); + } + const projectionHash = canonicalHash(SNAPSHOT_PROJECTION_HASH_TAG, projection); + if (expectedHash !== void 0 && scopeHashValue(expectedHash, "Snapshot projection hash") !== projectionHash) { + throw new Error("Native snapshot projection content hash mismatch"); + } + return projection; +} +function parseDeclaredArtifact(value, index) { + const artifact = scopeRecord(value, `Declared artifact ${index}`); + exactScopeKeys(artifact, ["path", "kind"], [], `Declared artifact ${index}`); + if (artifact.kind !== "file" && artifact.kind !== "directory") { + throw new Error(`Declared artifact ${index} kind is invalid`); + } + return { + path: projectRelativePath(artifact.path, `Declared artifact ${index} path`), + kind: artifact.kind + }; +} +function parseFileIdentity(value, label) { + if (value === null) return null; + const identity = scopeRecord(value, label); + exactScopeKeys(identity, ["hash", "size"], [], label); + return { + hash: scopeHashValue(identity.hash, `${label} hash`), + size: nonNegativeSafeInteger(identity.size, `${label} size`) + }; +} +function parseImplementationChange(value, index) { + const change = scopeRecord(value, `Implementation change ${index}`); + exactScopeKeys( + change, + ["path", "kind", "before", "after", "attributedTo"], + [], + `Implementation change ${index}` + ); + if (change.kind !== "added" && change.kind !== "modified" && change.kind !== "removed") { + throw new Error(`Implementation change ${index} kind is invalid`); + } + if (!Array.isArray(change.attributedTo)) { + throw new Error(`Implementation change ${index} attributedTo must be an array`); + } + const attributedTo = change.attributedTo.map(parseDeclaredArtifact); + const normalizedAttribution = normalizeDeclaredArtifacts(attributedTo); + if (JSON.stringify(attributedTo) !== JSON.stringify(normalizedAttribution)) { + throw new Error(`Implementation change ${index} attribution must be sorted and unique`); + } + const before = parseFileIdentity(change.before, `Implementation change ${index} before`); + const after = parseFileIdentity(change.after, `Implementation change ${index} after`); + if (change.kind === "added" && (before !== null || after === null) || change.kind === "removed" && (before === null || after !== null) || change.kind === "modified" && (before === null || after === null || JSON.stringify(before) === JSON.stringify(after))) { + throw new Error(`Implementation change ${index} before/after state is invalid`); + } + return { + path: projectRelativePath(change.path, `Implementation change ${index} path`), + kind: change.kind, + before, + after, + attributedTo + }; +} +function parseUnresolvedScope(value, index) { + const scope = scopeRecord(value, `Unresolved scope ${index}`); + exactScopeKeys( + scope, + ["id", "kind", "source", "path", "reason"], + [], + `Unresolved scope ${index}` + ); + const kinds = /* @__PURE__ */ new Set([ + "unattributed-change", + "snapshot-omission", + "snapshot-incomplete", + "snapshot-omission-overflow", + "scope-detail-overflow", + "missing-no-code-reason" + ]); + if (typeof scope.kind !== "string" || !kinds.has(scope.kind)) { + throw new Error(`Unresolved scope ${index} kind is invalid`); + } + if (scope.source !== "baseline" && scope.source !== "current" && scope.source !== "implementation-scope") { + throw new Error(`Unresolved scope ${index} source is invalid`); + } + if (typeof scope.id !== "string" || !/^scope:[a-f0-9]{64}$/u.test(scope.id)) { + throw new Error(`Unresolved scope ${index} id is invalid`); + } + if (scope.path !== null && typeof scope.path !== "string") { + throw new Error(`Unresolved scope ${index} path is invalid`); + } + if (typeof scope.reason !== "string" || scope.reason.length === 0 || scope.reason.trim() !== scope.reason) { + throw new Error(`Unresolved scope ${index} reason is invalid`); + } + return { + id: scope.id, + kind: scope.kind, + source: scope.source, + path: scope.path === null ? null : scope.kind === "snapshot-omission" ? snapshotOmissionPath(scope.path, `Unresolved scope ${index} path`) : projectRelativePath(scope.path, `Unresolved scope ${index} path`), + reason: scope.reason + }; +} +function parseSortedPaths(value, label) { + if (!Array.isArray(value) || value.some((entry2) => typeof entry2 !== "string")) { + throw new Error(`${label} must be an array of paths`); + } + const paths = value.map((entry2, index) => projectRelativePath(entry2, `${label} ${index}`)); + const normalized = [...new Set(paths)].sort(compareText3); + if (JSON.stringify(paths) !== JSON.stringify(normalized)) { + throw new Error(`${label} must be sorted and unique`); + } + return paths; +} +function parseNativeImplementationScope(value) { + const root = scopeRecord(value, "Native implementation scope"); + const required = [...SCOPE_KEYS].filter((key) => key !== "gitAdvisory"); + exactScopeKeys(root, required, ["gitAdvisory"], "Native implementation scope"); + if (root.schema !== NATIVE_IMPLEMENTATION_SCOPE_SCHEMA) { + throw new Error("Native implementation scope schema is invalid"); + } + const contractHash = scopeHashValue(root.contractHash, "Implementation scope contractHash"); + const baselineProjectionHash = scopeHashValue( + root.baselineProjectionHash, + "Implementation scope baselineProjectionHash" + ); + const baselineProjectionRef = snapshotProjectionRefValue( + root.baselineProjectionRef, + baselineProjectionHash, + "Implementation scope baseline projection" + ); + const currentProjectionHash2 = scopeHashValue( + root.currentProjectionHash, + "Implementation scope currentProjectionHash" + ); + const currentProjectionRef = snapshotProjectionRefValue( + root.currentProjectionRef, + currentProjectionHash2, + "Implementation scope current projection" + ); + if (typeof root.complete !== "boolean") { + throw new Error("Implementation scope complete flag is invalid"); + } + if (!Array.isArray(root.declaredArtifacts) || !Array.isArray(root.changes) || !Array.isArray(root.unattributed) || !Array.isArray(root.unresolvedScopes)) { + throw new Error("Implementation scope collections are invalid"); + } + const declaredArtifacts = root.declaredArtifacts.map(parseDeclaredArtifact); + if (JSON.stringify(declaredArtifacts) !== JSON.stringify(normalizeDeclaredArtifacts(declaredArtifacts))) { + throw new Error("Implementation scope declared artifacts must be sorted and unique"); + } + const changes = root.changes.map(parseImplementationChange); + const sortedChanges = [...changes].sort((left, right) => compareText3(left.path, right.path)); + if (JSON.stringify(changes) !== JSON.stringify(sortedChanges) || new Set(changes.map((change) => change.path)).size !== changes.length) { + throw new Error("Implementation scope changes must be sorted and unique"); + } + const declaredByIdentity = new Set( + declaredArtifacts.map((artifact) => `${artifact.kind}:${artifact.path}`) + ); + if (changes.some( + (change) => change.attributedTo.some( + (artifact) => !declaredByIdentity.has(`${artifact.kind}:${artifact.path}`) + ) + )) { + throw new Error("Implementation scope change references an undeclared artifact"); + } + if (changes.some( + (change) => JSON.stringify(change.attributedTo) !== JSON.stringify( + declaredArtifacts.filter((artifact) => artifactOwnsPath(artifact, change.path)) + ) + )) { + throw new Error("Implementation scope change attribution is inconsistent"); + } + const unattributed = root.unattributed.map(parseImplementationChange); + const expectedUnattributed = changes.filter((change) => change.attributedTo.length === 0); + if (JSON.stringify(unattributed) !== JSON.stringify(expectedUnattributed)) { + throw new Error("Implementation scope unattributed changes are inconsistent"); + } + const unresolvedScopes = root.unresolvedScopes.map(parseUnresolvedScope); + const detailOverflowScopes = unresolvedScopes.filter( + (scope) => scope.kind === "scope-detail-overflow" + ); + if (detailOverflowScopes.length > 1) { + throw new Error("Implementation scope has multiple detail overflow records"); + } + const detailOverflow = detailOverflowScopes[0] === void 0 ? null : parseScopeDetailOverflow(detailOverflowScopes[0]); + if (detailOverflow && (detailOverflow.changes + detailOverflow.unresolved === 0 || detailOverflow.unattributed > detailOverflow.changes || detailOverflow.unattributed > detailOverflow.unresolved)) { + throw new Error("Implementation scope detail overflow counts are inconsistent"); + } + const noCodeReason = root.noCodeReason; + const expectedDerivedScopes = [ + ...expectedUnattributed.map( + (change) => unresolvedScope( + { + kind: "unattributed-change", + source: "implementation-scope", + path: change.path, + evidence: { + after: change.after, + before: change.before, + changeKind: change.kind + } + }, + `Changed path is not covered by a declared artifact: ${change.path}` + ) + ), + ...changes.length + (detailOverflow?.changes ?? 0) === 0 && noCodeReason === null ? [ + unresolvedScope( + { + kind: "missing-no-code-reason", + source: "implementation-scope", + path: null, + evidence: { baselineProjectionHash, currentProjectionHash: currentProjectionHash2 } + }, + "A non-empty no-code reason is required when the snapshots contain no changes" + ) + ] : [] + ]; + const actualDerivedScopes = unresolvedScopes.filter( + (scope) => scope.kind === "unattributed-change" || scope.kind === "missing-no-code-reason" + ); + if (JSON.stringify(unresolvedScopes) !== JSON.stringify([...unresolvedScopes].sort(compareUnresolvedScopes)) || new Set(unresolvedScopes.map((scope) => scope.id)).size !== unresolvedScopes.length || JSON.stringify(actualDerivedScopes) !== JSON.stringify(expectedDerivedScopes.sort(compareUnresolvedScopes)) || root.complete !== (unresolvedScopes.length === 0)) { + throw new Error("Implementation scope unresolved scopes are inconsistent"); + } + if (root.noCodeReason !== null && (typeof root.noCodeReason !== "string" || root.noCodeReason.length === 0 || root.noCodeReason.trim() !== root.noCodeReason)) { + throw new Error("Implementation scope no-code reason is invalid"); + } + let gitAdvisory; + if (root.gitAdvisory !== void 0) { + const advisory = scopeRecord(root.gitAdvisory, "Implementation scope Git advisory"); + exactScopeKeys( + advisory, + [ + "advisoryOnly", + "changedPaths", + "pathsPresentInSnapshotChanges", + "pathsAbsentFromSnapshotChanges" + ], + [], + "Implementation scope Git advisory" + ); + if (advisory.advisoryOnly !== true) { + throw new Error("Implementation scope Git advisory must remain advisory-only"); + } + const changedPaths = parseSortedPaths(advisory.changedPaths, "Git changed paths"); + const pathsPresentInSnapshotChanges = parseSortedPaths( + advisory.pathsPresentInSnapshotChanges, + "Git present paths" + ); + const pathsAbsentFromSnapshotChanges = parseSortedPaths( + advisory.pathsAbsentFromSnapshotChanges, + "Git absent paths" + ); + const partition = [...pathsPresentInSnapshotChanges, ...pathsAbsentFromSnapshotChanges].sort( + compareText3 + ); + const detailedChangePaths = new Set(changes.map((change) => change.path)); + if (JSON.stringify(partition) !== JSON.stringify(changedPaths) || (detailOverflow?.changes ?? 0) === 0 && pathsPresentInSnapshotChanges.some((entry2) => !detailedChangePaths.has(entry2)) || pathsAbsentFromSnapshotChanges.some((entry2) => detailedChangePaths.has(entry2))) { + throw new Error("Implementation scope Git advisory partition is invalid"); + } + gitAdvisory = { + advisoryOnly: true, + changedPaths, + pathsPresentInSnapshotChanges, + pathsAbsentFromSnapshotChanges + }; + } + const content = { + schema: NATIVE_IMPLEMENTATION_SCOPE_SCHEMA, + contractHash, + baselineProjectionRef, + baselineProjectionHash, + currentProjectionRef, + currentProjectionHash: currentProjectionHash2, + complete: root.complete, + declaredArtifacts, + changes, + unattributed, + unresolvedScopes, + noCodeReason, + ...gitAdvisory ? { gitAdvisory } : {} + }; + const scopeHash = scopeHashValue(root.scopeHash, "Implementation scope scopeHash"); + if (canonicalHash(IMPLEMENTATION_SCOPE_HASH_TAG, content) !== scopeHash) { + throw new Error("Implementation scope content hash mismatch"); + } + return { ...content, scopeHash }; +} +function parseScopeAuthority(value) { + const root = scopeRecord(value, "Native implementation scope authority"); + exactScopeKeys( + root, + ["contractHash", "declaredArtifacts", "noCodeReason"], + ["gitChangedPaths"], + "Native implementation scope authority" + ); + if (!Array.isArray(root.declaredArtifacts)) { + throw new Error("Native implementation scope authority declarations must be an array"); + } + const declaredArtifacts = root.declaredArtifacts.map(parseDeclaredArtifact); + if (JSON.stringify(declaredArtifacts) !== JSON.stringify(normalizeDeclaredArtifacts(declaredArtifacts))) { + throw new Error("Native implementation scope authority declarations must be canonical"); + } + if (root.noCodeReason !== null && (typeof root.noCodeReason !== "string" || root.noCodeReason.length === 0 || root.noCodeReason.trim() !== root.noCodeReason)) { + throw new Error("Native implementation scope authority no-code reason is invalid"); + } + let gitChangedPaths; + if (root.gitChangedPaths !== void 0) { + gitChangedPaths = parseSortedPaths( + root.gitChangedPaths, + "Native implementation scope authority Git paths" + ); + } + const authority = normalizeScopeAuthority({ + contractHash: root.contractHash, + declaredArtifacts, + noCodeReason: root.noCodeReason, + ...gitChangedPaths === void 0 ? {} : { gitChangedPaths } + }); + if (authority.contractHash !== root.contractHash) { + throw new Error("Native implementation scope authority contract hash is not canonical"); + } + return authority; +} +function parseNativeImplementationScopeBundle(value) { + const root = scopeRecord(value, "Native implementation scope bundle"); + exactScopeKeys( + root, + ["authority", "baseline", "current", "scope"], + [], + "Native implementation scope bundle" + ); + const authority = parseScopeAuthority(root.authority); + const baseline = parseNativeSnapshotProjection(root.baseline); + const current = parseNativeSnapshotProjection(root.current); + const suppliedScope = parseNativeImplementationScope(root.scope); + const rebuiltScope = buildScopeFromProjections(baseline, current, authority); + if (JSON.stringify(suppliedScope) !== JSON.stringify(rebuiltScope)) { + throw new Error("Native implementation scope does not match its authoritative bundle"); + } + return { authority, baseline, current, scope: rebuiltScope }; +} +function rebuildNativeImplementationScopeBundle(input) { + const suppliedScope = parseNativeImplementationScope(input.scope); + return parseNativeImplementationScopeBundle({ + authority: { + contractHash: suppliedScope.contractHash, + declaredArtifacts: suppliedScope.declaredArtifacts, + noCodeReason: suppliedScope.noCodeReason, + ...suppliedScope.gitAdvisory ? { gitChangedPaths: suppliedScope.gitAdvisory.changedPaths } : {} + }, + baseline: input.baseline, + current: input.current, + scope: suppliedScope + }); +} + +// domains/comet-native/native-verification-evidence.ts +import path21 from "node:path"; + +// domains/comet-native/native-redaction.ts +var AUTHORIZATION_PATTERN = /\b(Bearer|Basic)\s+[^\s"']+/giu; +var PRIVATE_KEY_PATTERN = /-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/gu; +var URI_CREDENTIAL_PATTERN = /(https?:\/\/)[^\s/@:]+:[^\s/@]+@/giu; +var KNOWN_TOKEN_PATTERN = /\b(?:sk-(?:proj-)?[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9_]{16,}|github_pat_[A-Za-z0-9_]{16,}|AKIA[A-Z0-9]{16}|AIza[A-Za-z0-9_-]{20,})\b/gu; +var JSON_CREDENTIAL_PATTERN = /("(?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie)"\s*:\s*)"(?:\\[\s\S]|[^"\\\r\n])*"/giu; +var QUOTED_CREDENTIAL_PATTERN = /(["'])((?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie))\1(\s*:\s*)(["'])[^\r\n]*?\4/giu; +var CREDENTIAL_ASSIGNMENT_PATTERN = /\b((?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret(?:[_-]?access)?[_-]?key|private[_-]?key|token|password|passwd|secret|authorization|cookie|set[_-]?cookie))\b(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;"']+)/giu; +function redactNativeCredentialText(value) { + return value.replace(AUTHORIZATION_PATTERN, "$1 [REDACTED]").replace(PRIVATE_KEY_PATTERN, "[REDACTED PRIVATE KEY]").replace(URI_CREDENTIAL_PATTERN, "$1[REDACTED]@").replace(KNOWN_TOKEN_PATTERN, "[REDACTED TOKEN]").replace(JSON_CREDENTIAL_PATTERN, '$1"[REDACTED]"').replace(QUOTED_CREDENTIAL_PATTERN, "$1$2$1$3$4[REDACTED]$4").replace(CREDENTIAL_ASSIGNMENT_PATTERN, "$1$2[REDACTED]"); +} + +// domains/comet-native/native-verification-evidence.ts +var HASH_PATTERN6 = /^[a-f0-9]{64}$/u; +var MISSING_ACCEPTANCE_DETAIL_LIMIT = 8; +var ACCEPTANCE_TRACE_HASH_TAG = "comet.native.acceptance-trace.v1"; +var PARTIAL_ALLOWANCE_HASH_TAG = "comet.native.partial-allowance.v1"; +var VERIFICATION_ENVELOPE_HASH_TAG = "comet.native.verification-evidence.v1"; +function hash2(value, label) { + if (!HASH_PATTERN6.test(value)) throw new Error(`${label} must be a SHA-256 hash`); + return value; +} +function positiveRevision2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native evidence source revision must be a positive integer"); + } + return value; +} +function changeName(value) { + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(value)) { + throw new Error(`Invalid Native evidence change name: ${value}`); + } + return value; +} +function requiredText(value, label, max = 2e3) { + const normalized = redactNativeCredentialText(value).trim(); + if (normalized.length === 0 || normalized.length > max) { + throw new Error(`${label} must be between 1 and ${max} characters`); + } + return normalized; +} +function portableRef(value, label) { + const normalized = path21.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path21.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized relative ref`); + } + return value; +} +function portableEvidenceRef(value, label, nativeRootRef3) { + const reference = portableRef(value, label); + const sensitiveReason = nativeSensitiveRelativePathReason(reference); + const lowerReference = reference.toLowerCase(); + const lowerNativeRoot = nativeRootRef3 ? portableRef(nativeRootRef3, "Native root ref").toLowerCase() : null; + if (sensitiveReason || lowerReference === "runtime" || lowerReference.startsWith("runtime/") || lowerNativeRoot !== null && (lowerReference === lowerNativeRoot || lowerReference.startsWith(`${lowerNativeRoot}/`))) { + throw new Error( + `${label} is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${reference}` + ); + } + return reference; +} +function checkReceiptRef(value) { + const reference = portableRef(value, "Verification receipt ref"); + if (!/^runtime\/evidence\/check-receipts\/[a-f0-9]{64}\.json$/u.test(reference)) { + throw new Error("Verification receipt ref must identify a Native check receipt"); + } + return reference; +} +function timestamp(value) { + const result2 = value.toISOString(); + if (Number.isNaN(Date.parse(result2))) throw new Error("Native evidence timestamp is invalid"); + return result2; +} +function acceptanceCriteriaHash(criteria) { + return canonicalHash( + "comet.native.acceptance-set.v1", + [...criteria].sort((left, right) => compareText4(left.id, right.id)) + ); +} +function compareText4(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function buildNativeAcceptanceEvidenceTrace(criteria, evidence, options) { + const nativeRootRef3 = portableRef(options.nativeRootRef, "Native root ref"); + const byId = new Map(criteria.map((criterion2) => [criterion2.id, criterion2])); + if (byId.size !== criteria.length) + throw new Error("Native contract has duplicate acceptance IDs"); + const evidenceById = /* @__PURE__ */ new Map(); + for (const entry2 of evidence) { + if (!byId.has(entry2.acceptance_id)) { + throw new Error(`Verification references unknown acceptance ID: ${entry2.acceptance_id}`); + } + if (evidenceById.has(entry2.acceptance_id)) { + throw new Error(`Verification repeats acceptance ID: ${entry2.acceptance_id}`); + } + evidenceById.set(entry2.acceptance_id, entry2); + } + const missing = [...byId.keys()].filter((id) => !evidenceById.has(id)); + if (missing.length > 0) { + const shown = missing.slice(0, MISSING_ACCEPTANCE_DETAIL_LIMIT); + const remainder = missing.length - shown.length; + throw new Error( + `Verification is missing ${missing.length} acceptance evidence entr${missing.length === 1 ? "y" : "ies"}: ${shown.join(", ")}${remainder > 0 ? `, ... (${remainder} more)` : ""}` + ); + } + const entries = [...byId.values()].sort((left, right) => compareText4(left.id, right.id)).map((criterion2) => { + const entry2 = evidenceById.get(criterion2.id); + const evidenceRefs = [...entry2.evidence_refs].map( + (reference) => portableEvidenceRef(reference, `Evidence ref for ${criterion2.id}`, nativeRootRef3) + ).sort(); + if (new Set(evidenceRefs).size !== evidenceRefs.length) { + throw new Error(`Verification repeats an evidence ref for ${criterion2.id}`); + } + const rawSkippedReason = entry2.skipped_reason?.trim() || null; + const skippedReason = rawSkippedReason === null ? null : requiredText(rawSkippedReason, `Skipped reason for ${criterion2.id}`); + if (evidenceRefs.length === 0 === (skippedReason === null)) { + throw new Error( + `Acceptance ${criterion2.id} requires exactly one of evidence refs or skipped reason` + ); + } + return { + acceptanceId: criterion2.id, + kind: criterion2.kind, + source: portableRef(criterion2.source, `Acceptance source for ${criterion2.id}`), + evidenceRefs, + skippedReason + }; + }); + const criteriaHash = acceptanceCriteriaHash(criteria); + const content = { + schema: "comet.native.acceptance-trace.v1", + nativeRootRef: nativeRootRef3, + criteriaHash, + total: entries.length, + evidenced: entries.filter((entry2) => entry2.evidenceRefs.length > 0).length, + skipped: entries.filter((entry2) => entry2.skippedReason !== null).length, + entries + }; + return { + ...content, + traceHash: canonicalHash(ACCEPTANCE_TRACE_HASH_TAG, content) + }; +} +function buildNativePartialAllowance(input) { + const scope = parseNativeImplementationScopeBundle(input.scopeBundle).scope; + if (scope.complete) throw new Error("Complete implementation scope cannot be partially allowed"); + const unresolved = new Set(scope.unresolvedScopes.map((entry2) => entry2.id)); + if (new Set(input.allowedScopeIds).size !== input.allowedScopeIds.length) { + throw new Error("Partial allowance has duplicate allowed scope IDs"); + } + const scopeIds = [...input.allowedScopeIds].sort(compareText4); + if (scopeIds.length === 0) throw new Error("Partial allowance requires at least one scope ID"); + if ([...unresolved, ...scopeIds].some((id) => !/^scope:[a-f0-9]{64}$/u.test(id))) { + throw new Error("Partial allowance scope IDs are invalid"); + } + const unknown = scopeIds.filter((id) => !unresolved.has(id)); + const missing = [...unresolved].filter((id) => !scopeIds.includes(id)); + if (unknown.length > 0) throw new Error(`Partial allowance has unknown scope IDs: ${unknown}`); + if (missing.length > 0) throw new Error(`Partial allowance is missing scope IDs: ${missing}`); + const content = { + schema: "comet.native.partial-allowance.v1", + change: changeName(input.change), + scopeHash: scope.scopeHash, + scopeIds, + reason: requiredText(input.reason, "Partial allowance reason"), + confirmedSummary: requiredText(input.confirmedSummary, "Partial allowance confirmation"), + sourceRevision: positiveRevision2(input.sourceRevision), + confirmedAt: timestamp(input.now ?? /* @__PURE__ */ new Date()) + }; + return { + ...content, + allowanceHash: canonicalHash(PARTIAL_ALLOWANCE_HASH_TAG, content) + }; +} +function buildNativeVerificationEvidenceEnvelope(input) { + if (input.result !== "pass" && input.result !== "fail") { + throw new Error("Native verification evidence result is invalid"); + } + const acceptanceTrace = parseNativeAcceptanceEvidenceTrace(input.acceptanceTrace); + const implementationScope = parseNativeImplementationScopeBundle( + input.implementationScope.bundle + ).scope; + const implementationScopeRef = evidenceDocumentRef( + input.implementationScope.ref, + "scopes", + implementationScope.scopeHash + ); + if (implementationScope.contractHash !== input.contractHash) { + throw new Error("Implementation scope does not match the verification contract"); + } + const allowanceInput = input.partialAllowance ?? null; + const parsedAllowance = allowanceInput ? parseNativePartialAllowance(allowanceInput.allowance) : null; + const allowance = allowanceInput && parsedAllowance ? { + ref: evidenceDocumentRef(allowanceInput.ref, "allowances", parsedAllowance.allowanceHash), + allowance: parsedAllowance + } : null; + if (implementationScope.complete && allowance !== null) { + throw new Error("Complete implementation scope must not use a partial allowance"); + } + if (!implementationScope.complete && allowance === null) { + throw new Error("Partial implementation scope requires a confirmed allowance"); + } + const unresolvedScopeIds = implementationScope.unresolvedScopes.map((entry2) => entry2.id).sort(compareText4); + if (allowance && (allowance.allowance.change !== input.change || allowance.allowance.scopeHash !== implementationScope.scopeHash || JSON.stringify(allowance.allowance.scopeIds) !== JSON.stringify(unresolvedScopeIds))) { + throw new Error("Partial allowance does not match the verification scope"); + } + if (acceptanceTrace.criteriaHash !== input.acceptanceHash) { + throw new Error("Acceptance trace does not match the verification contract"); + } + if (allowance && allowance.allowance.sourceRevision >= input.sourceRevision) { + throw new Error("Partial allowance must precede the verification evidence revision"); + } + const content = { + schema: "comet.native.verification-evidence.v1", + change: changeName(input.change), + sourceRevision: positiveRevision2(input.sourceRevision), + result: input.result, + freshness: implementationScope.complete ? "complete" : "partial", + contractHash: hash2(input.contractHash, "Verification contractHash"), + acceptanceCriteriaHash: hash2(input.acceptanceHash, "Verification acceptanceHash"), + implementationScopeRef, + implementationScopeHash: implementationScope.scopeHash, + reportRef: portableEvidenceRef(input.reportRef, "Verification report ref"), + reportHash: hash2(input.reportHash, "Verification report hash"), + acceptanceTrace, + partialAllowanceRef: allowance?.ref ?? null, + partialAllowanceHash: allowance?.allowance.allowanceHash ?? null, + receiptRef: input.receiptRef ? checkReceiptRef(input.receiptRef) : null, + createdAt: timestamp(input.now ?? /* @__PURE__ */ new Date()) + }; + return { + ...content, + envelopeHash: canonicalHash(VERIFICATION_ENVELOPE_HASH_TAG, content) + }; +} +function evidenceRecord(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactEvidenceKeys(value, keys, label) { + const expected = new Set(keys); + const unknown = Object.keys(value).filter((key) => !expected.has(key)); + const missing = keys.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function canonicalTimestamp(value, label) { + if (typeof value !== "string") throw new Error(`${label} is invalid`); + const parsed = new Date(value); + if (Number.isNaN(parsed.valueOf()) || parsed.toISOString() !== value) { + throw new Error(`${label} is invalid`); + } + return value; +} +function evidenceDocumentRef(value, kind, hash6) { + if (typeof value !== "string") throw new Error(`Native ${kind} evidence ref is invalid`); + const expected = `runtime/evidence/${kind}/${hash6}.json`; + if (value !== expected) throw new Error(`Native ${kind} evidence ref/hash mismatch`); + return value; +} +function parseNativeAcceptanceEvidenceTrace(value) { + const root = evidenceRecord(value, "Native acceptance trace"); + exactEvidenceKeys( + root, + [ + "schema", + "nativeRootRef", + "criteriaHash", + "total", + "evidenced", + "skipped", + "entries", + "traceHash" + ], + "Native acceptance trace" + ); + if (root.schema !== "comet.native.acceptance-trace.v1" || typeof root.nativeRootRef !== "string" || typeof root.criteriaHash !== "string" || !HASH_PATTERN6.test(root.criteriaHash) || !Number.isSafeInteger(root.total) || !Number.isSafeInteger(root.evidenced) || !Number.isSafeInteger(root.skipped) || !Array.isArray(root.entries)) { + throw new Error("Native acceptance trace is invalid"); + } + const nativeRootRef3 = portableRef(root.nativeRootRef, "Native root ref"); + const entries = root.entries.map((value2, index) => { + const entry2 = evidenceRecord(value2, `Native acceptance trace entry ${index}`); + exactEvidenceKeys( + entry2, + ["acceptanceId", "kind", "source", "evidenceRefs", "skippedReason"], + `Native acceptance trace entry ${index}` + ); + if (typeof entry2.acceptanceId !== "string" || !/^acceptance-[a-f0-9]{64}$/u.test(entry2.acceptanceId) || entry2.kind !== "brief-example" && entry2.kind !== "spec-scenario" || typeof entry2.source !== "string" || !Array.isArray(entry2.evidenceRefs) || entry2.evidenceRefs.some((reference) => typeof reference !== "string") || entry2.skippedReason !== null && (typeof entry2.skippedReason !== "string" || entry2.skippedReason.length === 0 || entry2.skippedReason.trim() !== entry2.skippedReason)) { + throw new Error(`Native acceptance trace entry ${index} is invalid`); + } + const evidenceRefs = entry2.evidenceRefs.map( + (reference) => portableEvidenceRef( + reference, + `Native acceptance trace entry ${index} evidence ref`, + nativeRootRef3 + ) + ); + if (JSON.stringify(evidenceRefs) !== JSON.stringify([...new Set(evidenceRefs)].sort(compareText4)) || evidenceRefs.length === 0 === (entry2.skippedReason === null)) { + throw new Error(`Native acceptance trace entry ${index} evidence state is invalid`); + } + return { + acceptanceId: entry2.acceptanceId, + kind: entry2.kind, + source: portableRef(entry2.source, `Native acceptance trace entry ${index} source`), + evidenceRefs, + skippedReason: entry2.skippedReason === null ? null : requiredText( + entry2.skippedReason, + `Native acceptance trace entry ${index} skipped reason` + ) + }; + }); + if (JSON.stringify(entries) !== JSON.stringify( + [...entries].sort((left, right) => compareText4(left.acceptanceId, right.acceptanceId)) + ) || new Set(entries.map((entry2) => entry2.acceptanceId)).size !== entries.length || root.total !== entries.length || root.evidenced !== entries.filter((entry2) => entry2.evidenceRefs.length > 0).length || root.skipped !== entries.filter((entry2) => entry2.skippedReason !== null).length) { + throw new Error("Native acceptance trace entries are inconsistent"); + } + const content = { + schema: "comet.native.acceptance-trace.v1", + nativeRootRef: nativeRootRef3, + criteriaHash: root.criteriaHash, + total: root.total, + evidenced: root.evidenced, + skipped: root.skipped, + entries + }; + const traceHash = hash2(root.traceHash, "Native acceptance trace hash"); + if (canonicalHash(ACCEPTANCE_TRACE_HASH_TAG, content) !== traceHash) { + throw new Error("Native acceptance trace content hash mismatch"); + } + return { ...content, traceHash }; +} +function parseNativePartialAllowance(value) { + const root = evidenceRecord(value, "Native partial allowance"); + exactEvidenceKeys( + root, + [ + "schema", + "change", + "scopeHash", + "scopeIds", + "reason", + "confirmedSummary", + "sourceRevision", + "confirmedAt", + "allowanceHash" + ], + "Native partial allowance" + ); + if (root.schema !== "comet.native.partial-allowance.v1" || typeof root.change !== "string" || typeof root.scopeHash !== "string" || !Array.isArray(root.scopeIds) || root.scopeIds.length === 0 || root.scopeIds.some((id) => typeof id !== "string" || !/^scope:[a-f0-9]{64}$/u.test(id)) || JSON.stringify(root.scopeIds) !== JSON.stringify([...new Set(root.scopeIds)].sort(compareText4)) || typeof root.reason !== "string" || typeof root.confirmedSummary !== "string") { + throw new Error("Native partial allowance is invalid"); + } + const content = { + schema: "comet.native.partial-allowance.v1", + change: changeName(root.change), + scopeHash: hash2(root.scopeHash, "Native partial allowance scopeHash"), + scopeIds: root.scopeIds, + reason: requiredText(root.reason, "Partial allowance reason"), + confirmedSummary: requiredText(root.confirmedSummary, "Partial allowance confirmation"), + sourceRevision: positiveRevision2(root.sourceRevision), + confirmedAt: canonicalTimestamp(root.confirmedAt, "Native partial allowance timestamp") + }; + if (content.reason !== root.reason || content.confirmedSummary !== root.confirmedSummary) { + throw new Error("Native partial allowance text is not canonical"); + } + const allowanceHash = hash2(root.allowanceHash, "Native partial allowance hash"); + if (canonicalHash(PARTIAL_ALLOWANCE_HASH_TAG, content) !== allowanceHash) { + throw new Error("Native partial allowance content hash mismatch"); + } + return { ...content, allowanceHash }; +} +function parseNativeVerificationEvidenceEnvelope(value) { + const root = evidenceRecord(value, "Native verification evidence"); + exactEvidenceKeys( + root, + [ + "schema", + "change", + "sourceRevision", + "result", + "freshness", + "contractHash", + "acceptanceCriteriaHash", + "implementationScopeRef", + "implementationScopeHash", + "reportRef", + "reportHash", + "acceptanceTrace", + "partialAllowanceRef", + "partialAllowanceHash", + "receiptRef", + "createdAt", + "envelopeHash" + ], + "Native verification evidence" + ); + if (root.schema !== "comet.native.verification-evidence.v1" || typeof root.change !== "string" || root.result !== "pass" && root.result !== "fail" || root.freshness !== "complete" && root.freshness !== "partial" || typeof root.implementationScopeHash !== "string" || typeof root.reportRef !== "string" || root.receiptRef !== null && typeof root.receiptRef !== "string") { + throw new Error("Native verification evidence is invalid"); + } + const implementationScopeHash = hash2( + root.implementationScopeHash, + "Native verification implementation scope hash" + ); + const acceptanceTrace = parseNativeAcceptanceEvidenceTrace(root.acceptanceTrace); + const acceptanceCriteriaHash2 = hash2( + root.acceptanceCriteriaHash, + "Native verification acceptance criteria hash" + ); + if (acceptanceTrace.criteriaHash !== acceptanceCriteriaHash2) { + throw new Error("Native verification acceptance trace does not match its criteria hash"); + } + const hasAllowance = root.partialAllowanceRef !== null || root.partialAllowanceHash !== null; + if (root.partialAllowanceRef === null !== (root.partialAllowanceHash === null) || root.freshness === "complete" && hasAllowance || root.freshness === "partial" && !hasAllowance) { + throw new Error("Native verification partial allowance state is invalid"); + } + const partialAllowanceHash = root.partialAllowanceHash === null ? null : hash2(root.partialAllowanceHash, "Native verification allowance hash"); + const result2 = root.result; + const freshness = root.freshness; + const content = { + schema: "comet.native.verification-evidence.v1", + change: changeName(root.change), + sourceRevision: positiveRevision2(root.sourceRevision), + result: result2, + freshness, + contractHash: hash2(root.contractHash, "Native verification contract hash"), + acceptanceCriteriaHash: acceptanceCriteriaHash2, + implementationScopeRef: evidenceDocumentRef( + root.implementationScopeRef, + "scopes", + implementationScopeHash + ), + implementationScopeHash, + reportRef: portableEvidenceRef(root.reportRef, "Native verification report ref"), + reportHash: hash2(root.reportHash, "Native verification report hash"), + acceptanceTrace, + partialAllowanceRef: partialAllowanceHash === null ? null : evidenceDocumentRef(root.partialAllowanceRef, "allowances", partialAllowanceHash), + partialAllowanceHash, + receiptRef: root.receiptRef === null ? null : checkReceiptRef(root.receiptRef), + createdAt: canonicalTimestamp(root.createdAt, "Native verification timestamp") + }; + const envelopeHash = hash2(root.envelopeHash, "Native verification envelope hash"); + if (canonicalHash(VERIFICATION_ENVELOPE_HASH_TAG, content) !== envelopeHash) { + throw new Error("Native verification evidence content hash mismatch"); + } + return { ...content, envelopeHash }; +} + +// domains/comet-native/native-evidence-storage.ts +var HASH_PATTERN7 = /^[a-f0-9]{64}$/u; +var MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES = MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES; +var MAX_NATIVE_IMPLEMENTATION_SCOPE_BUNDLE_BYTES = 3 * MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES; +function isInside5(parent, target) { + const relative = path22.relative(parent, target); + return relative === "" || !path22.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path22.sep}`); +} +function sameDirectoryIdentity5(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity6(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryIdentity3(directory) { + const stat = await fs15.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native evidence parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs15.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain4(root, directory) { + const lexicalRoot = path22.resolve(root); + const lexicalDirectory = path22.resolve(directory); + if (!isInside5(lexicalRoot, lexicalDirectory)) { + throw new Error("Native evidence path is outside its change"); + } + const chain = [await captureDirectoryIdentity3(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path22.relative(lexicalRoot, lexicalDirectory).split(path22.sep).filter(Boolean)) { + cursor = path22.join(cursor, segment); + const identity = await captureDirectoryIdentity3(cursor); + if (!isInside5(chain[0].realPath, identity.realPath)) { + throw new Error(`Native evidence parent resolves outside its change: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain5(chain) { + for (const identity of chain) { + const stat = await fs15.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity5(identity, stat) || await fs15.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native evidence parent changed while reading: ${identity.path}`); + } + } +} +async function readBoundedEvidenceJson(file, changeRoot, hooks = {}) { + const chain = await captureDirectoryChain4(changeRoot, path22.dirname(file)); + await hooks.afterParentChainCaptured?.(); + const lexical = await fs15.lstat(file); + if (!lexical.isFile() || lexical.isSymbolicLink()) { + throw new Error("Native evidence document must be a regular file"); + } + const realPath = await fs15.realpath(file); + if (!isInside5(chain[0].realPath, realPath)) { + throw new Error("Native evidence document resolves outside its change"); + } + const handle = await fs15.open(file, "r"); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs15.lstat(file), + fs15.realpath(file) + ]); + await verifyDirectoryChain5(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== realPath || !sameFileIdentity6(opened, lexical) || !sameFileIdentity6(opened, pathAfterOpen)) { + throw new Error("Native evidence document changed while opening"); + } + if (opened.size > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error( + `Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes` + ); + } + await hooks.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(64 * 1024); + while (true) { + const remaining = MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error( + `Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes` + ); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + await hooks.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs15.lstat(file), + fs15.realpath(file) + ]); + await verifyDirectoryChain5(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity6(opened, afterHandle) || !sameFileIdentity6(opened, afterPath)) { + throw new Error("Native evidence document changed while reading"); + } + return JSON.parse(Buffer.concat(chunks, total).toString("utf8")); + } finally { + await handle.close(); + } +} +function nativeEvidenceRef(kind, hash6) { + if (!HASH_PATTERN7.test(hash6)) throw new Error("Native evidence hash is invalid"); + return `runtime/evidence/${kind}/${hash6}.json`; +} +function nativeReportEvidenceRef(hash6) { + return nativeEvidenceRef("reports", hash6); +} +async function writeNativeVerificationReportSnapshot(options) { + const encoded = Buffer.from(options.text, "utf8"); + if (encoded.byteLength > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES || createHash12("sha256").update(encoded).digest("hex") !== options.hash) { + throw new Error("Native verification report snapshot hash or size is invalid"); + } + return writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "reports", + hash: options.hash, + value: { + schema: "comet.native.verification-report.v1", + reportHash: options.hash, + content: options.text + } + }); +} +async function readNativeVerificationReportSnapshot(paths, name, hash6) { + if (!HASH_PATTERN7.test(hash6)) throw new Error("Native report evidence hash is invalid"); + const value = await readEvidenceDocument({ paths, name, kind: "reports", hash: hash6 }); + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native report evidence must be an object"); + } + const report = value; + if (Object.keys(report).sort().join(",") !== "content,reportHash,schema" || report.schema !== "comet.native.verification-report.v1" || report.reportHash !== hash6 || typeof report.content !== "string" || createHash12("sha256").update(Buffer.from(report.content, "utf8")).digest("hex") !== hash6) { + throw new Error("Native report evidence does not match its hash"); + } + return report.content; +} +function parseEvidenceRef(ref, expectedKind) { + const match = /^runtime\/evidence\/(snapshots|scopes|allowances|verifications|reports)\/([a-f0-9]{64})\.json$/u.exec( + ref + ); + if (!match || match[1] !== expectedKind) { + throw new Error(`Native evidence ref is invalid for ${expectedKind}`); + } + return match[2]; +} +function evidenceFile(paths, name, kind, hash6) { + return path22.join(nativeChangeDir(paths, name), ...nativeEvidenceRef(kind, hash6).split("/")); +} +async function readEvidenceDocument(options) { + const file = evidenceFile(options.paths, options.name, options.kind, options.hash); + await resolveContainedNativePath(options.paths.nativeRoot, file); + return readBoundedEvidenceJson(file, nativeChangeDir(options.paths, options.name), options.hooks); +} +async function writeEvidenceDocument(options) { + assertEvidenceDocumentBudget(options.value); + const file = evidenceFile(options.paths, options.name, options.kind, options.hash); + await resolveContainedNativePath(options.paths.nativeRoot, file); + try { + const existing = await readEvidenceDocument(options); + if (JSON.stringify(existing) !== JSON.stringify(options.value)) { + throw new Error(`Native evidence hash collision for ${options.hash}`); + } + return nativeEvidenceRef(options.kind, options.hash); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, options.value, { containedRoot: options.paths.nativeRoot }); + const persisted = await readEvidenceDocument(options); + if (JSON.stringify(persisted) !== JSON.stringify(options.value)) { + throw new Error(`Native evidence changed during commit for ${options.hash}`); + } + return nativeEvidenceRef(options.kind, options.hash); +} +function assertEvidenceDocumentBudget(value) { + if (serializedEvidenceBytes2(value) > MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES) { + throw new Error(`Native evidence document exceeds ${MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES} bytes`); + } +} +function serializedEvidenceBytes2(value) { + return Buffer.byteLength(JSON.stringify(value, null, 2) + "\n", "utf8"); +} +function parseSnapshot2(value, expectedHash) { + return parseNativeSnapshotProjection(value, expectedHash); +} +function parseScope(value, expectedHash) { + const scope = parseNativeImplementationScope(value); + if (scope.scopeHash !== expectedHash) { + throw new Error("Native implementation scope ref/hash mismatch"); + } + return scope; +} +function parseAllowance(value, expectedName, expectedHash) { + const allowance = parseNativePartialAllowance(value); + if (allowance.change !== expectedName || allowance.allowanceHash !== expectedHash) { + throw new Error("Native partial allowance ref/hash/change mismatch"); + } + return allowance; +} +function parseEnvelope(value, expectedName, expectedHash) { + const evidence = parseNativeVerificationEvidenceEnvelope(value); + if (evidence.change !== expectedName || evidence.envelopeHash !== expectedHash) { + throw new Error("Native verification evidence ref/hash/change mismatch"); + } + return evidence; +} +async function assertEnvelopeDependencies(paths, name, evidence, requireReportSnapshot = false) { + const scope = await readNativeImplementationScope(paths, name, evidence.implementationScopeRef); + if (requireReportSnapshot) { + await readNativeVerificationReportSnapshot(paths, name, evidence.reportHash); + } + if (scope.scopeHash !== evidence.implementationScopeHash || scope.contractHash !== evidence.contractHash || (scope.complete ? "complete" : "partial") !== evidence.freshness) { + throw new Error("Native verification evidence does not match its implementation scope"); + } + if (evidence.partialAllowanceRef === null) return; + const allowance = await readNativePartialAllowance(paths, name, evidence.partialAllowanceRef); + if (allowance.allowanceHash !== evidence.partialAllowanceHash || allowance.scopeHash !== scope.scopeHash || JSON.stringify(allowance.scopeIds) !== JSON.stringify(scope.unresolvedScopes.map((entry2) => entry2.id).sort()) || allowance.sourceRevision >= evidence.sourceRevision) { + throw new Error("Native verification evidence does not match its partial allowance"); + } +} +async function writeNativeImplementationScope(options) { + const bundle = parseNativeImplementationScopeBundle(options.bundle); + const { baseline, current, scope } = bundle; + assertEvidenceDocumentBudget(baseline); + assertEvidenceDocumentBudget(current); + assertEvidenceDocumentBudget(scope); + await writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "snapshots", + hash: scope.baselineProjectionHash, + value: baseline + }); + await writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "snapshots", + hash: scope.currentProjectionHash, + value: current + }); + return writeEvidenceDocument({ + paths: options.paths, + name: options.name, + kind: "scopes", + hash: scope.scopeHash, + value: scope + }); +} +async function readNativeImplementationScopeBundle(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "scopes"); + const scope = parseScope( + await readEvidenceDocument({ paths, name, kind: "scopes", hash: hash6, hooks }), + hash6 + ); + const baselineHash = parseEvidenceRef(scope.baselineProjectionRef, "snapshots"); + const currentHash = parseEvidenceRef(scope.currentProjectionRef, "snapshots"); + const [baseline, current] = await Promise.all([ + readEvidenceDocument({ paths, name, kind: "snapshots", hash: baselineHash }).then( + (value) => parseSnapshot2(value, baselineHash) + ), + readEvidenceDocument({ paths, name, kind: "snapshots", hash: currentHash }).then( + (value) => parseSnapshot2(value, currentHash) + ) + ]); + return rebuildNativeImplementationScopeBundle({ baseline, current, scope }); +} +async function readNativeImplementationScope(paths, name, ref, hooks) { + return (await readNativeImplementationScopeBundle(paths, name, ref, hooks)).scope; +} +async function writeNativePartialAllowance(options) { + const allowance = parseAllowance( + options.allowance, + options.name, + options.allowance.allowanceHash + ); + const scope = await readNativeImplementationScope( + options.paths, + options.name, + nativeEvidenceRef("scopes", allowance.scopeHash) + ); + const unresolvedScopeIds = scope.unresolvedScopes.map((entry2) => entry2.id).sort(); + if (scope.complete || JSON.stringify(unresolvedScopeIds) !== JSON.stringify(allowance.scopeIds)) { + throw new Error("Native partial allowance does not match a persisted partial scope"); + } + return writeEvidenceDocument({ + ...options, + kind: "allowances", + hash: allowance.allowanceHash, + value: allowance + }); +} +async function readNativePartialAllowance(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "allowances"); + return parseAllowance( + await readEvidenceDocument({ paths, name, kind: "allowances", hash: hash6, hooks }), + name, + hash6 + ); +} +async function writeNativeVerificationEvidence(options) { + const evidence = parseEnvelope(options.evidence, options.name, options.evidence.envelopeHash); + await assertEnvelopeDependencies(options.paths, options.name, evidence, true); + return writeEvidenceDocument({ + ...options, + kind: "verifications", + hash: evidence.envelopeHash, + value: evidence + }); +} +async function readNativeVerificationEvidence(paths, name, ref, hooks) { + const hash6 = parseEvidenceRef(ref, "verifications"); + const evidence = parseEnvelope( + await readEvidenceDocument({ paths, name, kind: "verifications", hash: hash6, hooks }), + name, + hash6 + ); + await assertEnvelopeDependencies(paths, name, evidence); + return evidence; +} + +// domains/comet-native/native-conflict-inspection.ts +async function visibleChangeEntries(paths) { + try { + const directory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory: paths.changesDir, + label: "Native conflict changes directory", + maxEntries: NATIVE_CONFLICT_RADAR_LIMITS.maxChanges + }); + await directory.verify(); + return directory.entries; + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } +} +async function collectConflictInput(paths, name) { + const state = await readNativeChange(paths, name); + const [scope, workspace] = await Promise.all([ + state.implementation_scope ? readNativeImplementationScope(paths, name, state.implementation_scope) : null, + // Workspace identity is advisory. A malformed advisory must not suppress deterministic + // capability/artifact conflict facts. + readNativeWorkspaceIdentity(paths, name).catch(() => null) + ]); + return { + name: state.name, + revision: state.revision, + specs: state.spec_changes.map((spec) => ({ + capability: spec.capability, + operation: spec.operation, + baseHash: spec.base_hash + })), + declaredArtifacts: scope?.declaredArtifacts ?? [], + workspaceIdentityHash: workspace?.nativeRootId ?? null + }; +} +async function collectConflictInputs(paths) { + const entries = await visibleChangeEntries(paths); + const names = entries.filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + return Promise.all(names.map((name) => collectConflictInput(paths, name))); +} +async function inspectNativeChangeConflicts(paths, name) { + const input = await collectConflictInputs(paths); + buildNativeConflictRadar(input); + const target = input.find((change) => change.name === name); + if (!target) throw new Error(`Native conflict target is not visible: ${name}`); + let definiteConflictCount = 0; + let possibleOverlapCount = 0; + for (const other of input) { + if (other.name === name) continue; + const relationship2 = buildNativeConflictRadar([target, other]).relationships[0]; + if (relationship2.classification === "definite-conflict") definiteConflictCount += 1; + if (relationship2.classification === "possible-overlap") possibleOverlapCount += 1; + } + return { + definiteConflictCount, + possibleOverlapCount, + findingCodes: [ + ...definiteConflictCount > 0 ? ["native-change-conflict"] : [], + ...possibleOverlapCount > 0 ? ["native-change-overlap"] : [] + ] + }; +} + +// domains/comet-native/native-transition-journal.ts +import { randomUUID as randomUUID3 } from "crypto"; +import { promises as fs16 } from "fs"; +import path23 from "path"; +import { isDeepStrictEqual } from "util"; + +// domains/comet-native/native-repair-stagnation.ts +var NATIVE_REPAIR_SIGNATURE_SCHEMA = "comet.native.repair-signature.v1"; +var NATIVE_REPAIR_STAGNATION_LIMITS = { + warningAtConsecutiveFailures: 2, + manualStopAtConsecutiveFailures: 3, + maxRepairIterations: 12, + maxHistoryRecords: 64, + maxCategories: 16, + maxFailedCheckIds: 128, + maxOverrideSummaryCharacters: 2e3 +}; +var SIGNATURE_HASH_TAG = "comet.native.repair-signature.v1"; +var OVERRIDE_SUMMARY_HASH_TAG = "comet.native.repair-override-summary.v1"; +var HASH_PATTERN8 = /^[a-f0-9]{64}$/u; +var TOKEN_PATTERN = /^[a-z0-9][a-z0-9._:/-]{0,255}$/u; +function compareText5(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function hash3(value, label) { + if (typeof value !== "string" || !HASH_PATTERN8.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function positiveInteger3(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function normalizedTokens(values, label, max, allowEmpty) { + if (!Array.isArray(values) || values.length > max || !allowEmpty && values.length === 0) { + throw new Error(`${label} exceeds its count boundary`); + } + const tokens = values.map((value) => { + if (typeof value !== "string" || !TOKEN_PATTERN.test(value)) { + throw new Error(`${label} contains an invalid token: ${String(value)}`); + } + return value; + }); + return [...new Set(tokens)].sort(compareText5); +} +function normalizeNativeRepairFailureTokens(options) { + const categories = options.categories && options.categories.length > 0 ? options.categories : ["verification-failed"]; + return { + categories: normalizedTokens( + categories, + "Native repair categories", + NATIVE_REPAIR_STAGNATION_LIMITS.maxCategories, + false + ), + failedCheckIds: normalizedTokens( + options.failedCheckIds ?? [], + "Native repair failed check IDs", + NATIVE_REPAIR_STAGNATION_LIMITS.maxFailedCheckIds, + true + ) + }; +} +function buildNativeRepairSignature(facts) { + const tokens = normalizeNativeRepairFailureTokens(facts); + const content = { + schema: NATIVE_REPAIR_SIGNATURE_SCHEMA, + contractHash: hash3(facts.contractHash, "Native repair contract hash"), + implementationScopeHash: hash3( + facts.implementationScopeHash, + "Native repair implementation scope hash" + ), + artifactSnapshotHash: hash3(facts.artifactSnapshotHash, "Native repair artifact snapshot hash"), + categories: tokens.categories, + failedCheckIds: tokens.failedCheckIds + }; + return { + ...content, + signatureHash: canonicalHash(SIGNATURE_HASH_TAG, content) + }; +} +function normalizeHistory(history) { + if (!Array.isArray(history) || history.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxHistoryRecords) { + throw new Error("Native repair history exceeds its record boundary"); + } + let previousIteration = 0; + let previousRevision = 0; + return history.map((record8, index) => { + if (!record8 || record8.kind !== "failure" && record8.kind !== "override") { + throw new Error(`Native repair history record ${index} is invalid`); + } + const expectedKeys = record8.kind === "failure" ? ["iteration", "kind", "revision", "signatureHash"] : ["iteration", "kind", "revision", "signatureHash", "summaryHash"]; + const keys = Object.keys(record8).sort(compareText5); + if (keys.length !== expectedKeys.length || keys.some((key, keyIndex) => key !== expectedKeys[keyIndex])) { + throw new Error(`Native repair history record ${index} fields are invalid`); + } + const iteration = positiveInteger3(record8.iteration, `Native repair history ${index} iteration`); + const revision = positiveInteger3(record8.revision, `Native repair history ${index} revision`); + const previous = index > 0 ? history[index - 1] : null; + const pairedOverride = record8.kind === "override" && previous?.kind === "failure" && iteration === previousIteration && revision >= previousRevision && record8.signatureHash === previous.signatureHash; + if (!pairedOverride && (iteration <= previousIteration || revision <= previousRevision)) { + throw new Error("Native repair history must be strictly ordered by revision and iteration"); + } + previousIteration = iteration; + previousRevision = revision; + const signatureHash = hash3(record8.signatureHash, `Native repair history ${index} signature`); + if (record8.kind === "failure") return { kind: "failure", iteration, revision, signatureHash }; + return { + kind: "override", + iteration, + revision, + signatureHash, + summaryHash: hash3(record8.summaryHash, `Native repair history ${index} summary`) + }; + }); +} +function overrideSummary(value) { + const summary = value.trim(); + if (summary.length === 0 || summary.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxOverrideSummaryCharacters) { + throw new Error("Native repair override summary is invalid"); + } + return summary; +} +function hashNativeRepairOverrideSummary(summary) { + return canonicalHash(OVERRIDE_SUMMARY_HASH_TAG, { + summary: overrideSummary(summary) + }); +} +function decideNativeRepairStagnation(options) { + const signature = buildNativeRepairSignature(options.facts); + const history = normalizeHistory(options.history); + const failures = history.filter( + (record8) => record8.kind === "failure" + ); + const totalRepairFailures = failures.length + 1; + const remainingIterations = Math.max( + 0, + NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations - totalRepairFailures + ); + let consecutiveFailures = 1; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== signature.signatureHash) break; + consecutiveFailures += 1; + } + if (totalRepairFailures >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + return { + disposition: "hard-stop", + reasonCode: "repair-iteration-limit", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (consecutiveFailures < NATIVE_REPAIR_STAGNATION_LIMITS.warningAtConsecutiveFailures) { + return { + disposition: "continue", + reasonCode: "new-failure-signature", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (consecutiveFailures < NATIVE_REPAIR_STAGNATION_LIMITS.manualStopAtConsecutiveFailures) { + return { + disposition: "warn", + reasonCode: "repeated-failure-warning", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; + } + if (options.override) { + const expected = hash3( + options.override.expectedSignatureHash, + "Native repair override expected signature" + ); + if (expected !== signature.signatureHash) { + throw new Error("Native repair override does not match the current failure signature"); + } + overrideSummary(options.override.summary); + const alreadyUsed2 = history.some( + (record8) => record8.kind === "override" && record8.signatureHash === signature.signatureHash + ); + return { + disposition: alreadyUsed2 ? "manual-stop" : "continue", + reasonCode: alreadyUsed2 ? "override-already-used" : "override-accepted", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: !alreadyUsed2 + }; + } + const alreadyUsed = history.some( + (record8) => record8.kind === "override" && record8.signatureHash === signature.signatureHash + ); + return { + disposition: "manual-stop", + reasonCode: alreadyUsed ? "override-already-used" : "repeated-failure-stop", + signature, + consecutiveFailures, + totalRepairFailures, + remainingIterations, + overrideAccepted: false + }; +} + +// domains/comet-native/native-trajectory-limits.ts +var NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS = 4096; +function assertNativeTrajectoryText(value, label) { + if (typeof value !== "string" || value.trim().length === 0 || value.length > NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS) { + throw new Error( + `${label} must be between 1 and ${NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS} characters` + ); + } +} + +// domains/comet-native/native-repair-runtime.ts +var NATIVE_REPAIR_TRAJECTORY_FIELD = "repairStagnation"; +var NATIVE_REPAIR_TRAJECTORY_LIMITS = { + maxEvents: 4096, + maxDataDepth: 8, + maxDataNodes: 4096, + maxTotalDataNodes: 65536, + maxObjectFields: 64, + maxArrayEntries: 256, + maxKeyCharacters: 128, + maxTextCharacters: NATIVE_TRAJECTORY_MAX_TEXT_CHARACTERS, + maxEventDataCharacters: 65536, + maxTotalDataCharacters: 1048576, + maxRunIdCharacters: 256 +}; +var HASH_PATTERN9 = /^[a-f0-9]{64}$/u; +var REPAIR_SCOPE_HASH_TAG = "comet.native.repair-scope.v1"; +var EVENT_TYPES2 = /* @__PURE__ */ new Set([ + "run_started", + "action_proposed", + "action_completed", + "eval_completed", + "checkpoint", + "state_migrated", + "state_transitioned", + "command_check_recorded", + "recovery_reconciled" +]); +var EVENT_KEYS2 = ["data", "runId", "sequence", "timestamp", "type"]; +var PROJECTION_KEYS = ["disposition", "overrideSummaryHash", "signatureHash"]; +function compareText6(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function exactKeys(value, expected, label) { + const actual = Object.keys(value).sort(compareText6); + if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) { + throw new Error(`${label} fields are invalid`); + } +} +function record5(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + const prototype = Object.getPrototypeOf(value); + if (prototype !== Object.prototype && prototype !== null) { + throw new Error(`${label} must be a plain object`); + } + return value; +} +function hash4(value, label) { + if (typeof value !== "string" || !HASH_PATTERN9.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function nativeRepairScopeHash(bundle) { + const scope = parseNativeImplementationScopeBundle(bundle).scope; + return canonicalHash(REPAIR_SCOPE_HASH_TAG, { + schema: REPAIR_SCOPE_HASH_TAG, + contractHash: scope.contractHash, + artifactSnapshotHash: scope.currentProjectionHash + }); +} +function boundedRunId(value) { + if (typeof value !== "string" || value.length === 0 || value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxRunIdCharacters || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + })) { + throw new Error("Native repair trajectory run ID is invalid"); + } + return value; +} +function boundedData(value, depth, budget, label, legacyTransitionText = false) { + budget.nodes += 1; + if (budget.nodes > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxDataNodes) { + throw new Error("Native repair trajectory event data exceeds its node boundary"); + } + if (depth > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxDataDepth) { + throw new Error("Native repair trajectory event data exceeds its depth boundary"); + } + if (value === null || typeof value === "boolean") return; + if (typeof value === "number") { + if (!Number.isFinite(value)) { + throw new Error(`${label} contains a non-finite number`); + } + return; + } + if (typeof value === "string") { + if (value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTextCharacters && (!legacyTransitionText || value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters)) { + throw new Error(`${label} contains oversized text`); + } + budget.characters += value.length; + if (budget.characters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters) { + throw new Error("Native repair trajectory event data exceeds its text boundary"); + } + return; + } + if (typeof value !== "object") { + throw new Error(`${label} contains a non-JSON value`); + } + if (budget.ancestors.has(value)) throw new Error(`${label} contains a cycle`); + budget.ancestors.add(value); + try { + if (Array.isArray(value)) { + if (value.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxArrayEntries) { + throw new Error(`${label} contains an oversized array`); + } + value.forEach((entry2, index) => boundedData(entry2, depth + 1, budget, `${label}[${index}]`)); + return; + } + const object = record5(value, label); + const keys = Object.keys(object); + if (keys.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxObjectFields) { + throw new Error(`${label} contains too many fields`); + } + for (const key of keys) { + if (key.length === 0 || key.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxKeyCharacters) { + throw new Error(`${label} contains an invalid field name`); + } + budget.characters += key.length; + if (budget.characters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEventDataCharacters) { + throw new Error("Native repair trajectory event data exceeds its text boundary"); + } + boundedData( + object[key], + depth + 1, + budget, + `${label}.${key}`, + depth === 0 && (key === "summary" || key === "noCodeReason") + ); + } + } finally { + budget.ancestors.delete(value); + } +} +function parseTimestamp(value) { + if (typeof value !== "string" || value.length > 64) { + throw new Error("Native repair trajectory timestamp is invalid"); + } + const parsed = new Date(value); + if (!Number.isFinite(parsed.valueOf()) || parsed.toISOString() !== value) { + throw new Error("Native repair trajectory timestamp is invalid"); + } + return value; +} +function parseEvent2(value, index, runId) { + const event = record5(value, `Native repair trajectory event ${index + 1}`); + exactKeys(event, EVENT_KEYS2, `Native repair trajectory event ${index + 1}`); + if (!Number.isSafeInteger(event.sequence) || event.sequence !== index + 1) { + throw new Error("Native repair trajectory sequence is invalid"); + } + parseTimestamp(event.timestamp); + if (typeof event.type !== "string" || !EVENT_TYPES2.has(event.type)) { + throw new Error("Native repair trajectory event type is invalid"); + } + if (boundedRunId(event.runId) !== runId) { + throw new Error("Native repair trajectory run ID changed inside the committed prefix"); + } + const budget = { nodes: 0, characters: 0, ancestors: /* @__PURE__ */ new Set() }; + boundedData(event.data, 0, budget, "Native trajectory event data"); + record5(event.data, "Native repair trajectory event data"); + return { + event, + dataNodes: budget.nodes, + dataCharacters: budget.characters + }; +} +function parseNativeRepairTrajectoryProjection(value) { + const projection = record5(value, "Native repair trajectory projection"); + exactKeys(projection, PROJECTION_KEYS, "Native repair trajectory projection"); + const signatureHash = hash4(projection.signatureHash, "Native repair trajectory signature hash"); + if (projection.disposition !== "continue" && projection.disposition !== "warn" && projection.disposition !== "manual-stop" && projection.disposition !== "hard-stop") { + throw new Error("Native repair trajectory disposition is invalid"); + } + const overrideSummaryHash = projection.overrideSummaryHash === null ? null : hash4(projection.overrideSummaryHash, "Native repair trajectory override summary hash"); + if (overrideSummaryHash !== null && projection.disposition !== "continue") { + throw new Error("Native repair trajectory override must continue exactly once"); + } + return { + signatureHash, + disposition: projection.disposition, + overrideSummaryHash + }; +} +function assertProjectionTransition(data, projection) { + if (projection.overrideSummaryHash === null) { + if (data.previousPhase !== "verify" || data.nextPhase !== "build" || data.verificationResult !== "fail") { + throw new Error( + "Native repair failure projection is only valid on a failed Verify-to-Build transition" + ); + } + return; + } + if (projection.disposition !== "continue" || data.previousPhase !== "build" || data.nextPhase !== "verify" || data.verificationResult !== null) { + throw new Error( + "Native repair override projection is only valid on a Build-to-Verify transition" + ); + } +} +function assertCommittedFailureProjection(projection, history) { + const failures = history.filter((entry2) => entry2.kind === "failure"); + const total = failures.length + 1; + if (total > NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + throw new Error("Native repair trajectory commits a failure beyond the hard-stop boundary"); + } + let consecutive = 1; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== projection.signatureHash) break; + consecutive += 1; + } + const expectedDisposition = total >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations ? "hard-stop" : consecutive < NATIVE_REPAIR_STAGNATION_LIMITS.warningAtConsecutiveFailures ? "continue" : consecutive < NATIVE_REPAIR_STAGNATION_LIMITS.manualStopAtConsecutiveFailures ? "warn" : "manual-stop"; + if (projection.disposition !== expectedDisposition || projection.overrideSummaryHash !== null) { + throw new Error( + `Native repair trajectory failure disposition is invalid: expected ${expectedDisposition}` + ); + } +} +function assertCommittedOverrideProjection(projection, history, latestProjection) { + const failures = history.filter((entry2) => entry2.kind === "failure"); + if (failures.length >= NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations) { + throw new Error("Native repair trajectory cannot override a hard stop"); + } + if (latestProjection?.disposition !== "manual-stop" || latestProjection.signatureHash !== projection.signatureHash || failures.at(-1)?.signatureHash !== projection.signatureHash) { + throw new Error("Native repair trajectory override does not match the latest manual stop"); + } + if (history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === projection.signatureHash + )) { + throw new Error("Native repair trajectory signature was already overridden"); + } +} +function projectNativeRepairHistory(options) { + if (!Array.isArray(options.trajectory) || options.trajectory.length > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxEvents) { + throw new Error("Native repair trajectory exceeds its event boundary"); + } + const runId = boundedRunId(options.runId); + if (!Number.isSafeInteger(options.committedTrajectoryOffset) || options.committedTrajectoryOffset < 0 || options.committedTrajectoryOffset > options.trajectory.length) { + throw new Error("Native repair committed trajectory offset is invalid"); + } + const history = []; + let latestProjection = null; + let activeScopeHash = null; + let iteration = 0; + let totalDataNodes = 0; + let totalDataCharacters = 0; + for (let index = 0; index < options.committedTrajectoryOffset; index += 1) { + const parsed = parseEvent2(options.trajectory[index], index, runId); + const { event } = parsed; + totalDataNodes += parsed.dataNodes; + totalDataCharacters += parsed.dataCharacters; + if (totalDataNodes > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTotalDataNodes || totalDataCharacters > NATIVE_REPAIR_TRAJECTORY_LIMITS.maxTotalDataCharacters) { + throw new Error("Native repair committed trajectory exceeds its aggregate data boundary"); + } + const data = event.data; + const eventScopeHash = Object.hasOwn(data, "repairScopeHash") ? hash4(data.repairScopeHash, "Native repair trajectory repair scope hash") : Object.hasOwn(data, "implementationScopeHash") ? hash4(data.implementationScopeHash, "Native repair trajectory implementation scope hash") : null; + if (!Object.hasOwn(data, NATIVE_REPAIR_TRAJECTORY_FIELD)) { + if (event.type === "state_transitioned" && data.previousPhase === "build" && data.nextPhase === "verify" && (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash || eventScopeHash === null && (latestProjection?.disposition === "manual-stop" || latestProjection?.disposition === "hard-stop"))) { + history.length = 0; + latestProjection = null; + activeScopeHash = eventScopeHash; + iteration = 0; + continue; + } + if (event.type === "state_transitioned" && data.previousPhase === "verify" && data.nextPhase === "archive" && data.verificationResult === "pass") { + history.length = 0; + latestProjection = null; + activeScopeHash = null; + iteration = 0; + } + continue; + } + if (event.type !== "state_transitioned") { + throw new Error("Native repair projection must belong to a state_transitioned event"); + } + const projection = parseNativeRepairTrajectoryProjection(data[NATIVE_REPAIR_TRAJECTORY_FIELD]); + assertProjectionTransition(data, projection); + if (projection.overrideSummaryHash === null) { + if (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash) { + history.length = 0; + iteration = 0; + } + if (eventScopeHash !== null) activeScopeHash = eventScopeHash; + assertCommittedFailureProjection(projection, history); + iteration += 1; + history.push({ + kind: "failure", + revision: event.sequence, + iteration, + signatureHash: projection.signatureHash + }); + } else { + if (eventScopeHash !== null && activeScopeHash !== null && eventScopeHash !== activeScopeHash) { + throw new Error("Native repair override cannot cross implementation scope progress"); + } + assertCommittedOverrideProjection(projection, history, latestProjection); + const failure = history.at(-1); + if (!failure || failure.kind !== "failure") { + throw new Error("Native repair trajectory override has no failure history"); + } + history.push({ + kind: "override", + revision: event.sequence, + iteration: failure.iteration, + signatureHash: projection.signatureHash, + summaryHash: projection.overrideSummaryHash + }); + } + latestProjection = projection; + if (history.length > NATIVE_REPAIR_STAGNATION_LIMITS.maxHistoryRecords) { + throw new Error("Native repair trajectory history exceeds its record boundary"); + } + } + return { history, latestProjection }; +} +function rebuildNativeRepairHistory(options) { + return projectNativeRepairHistory(options).history; +} +function inspectLatestNativeRepairProjection(options) { + const projection = projectNativeRepairHistory(options).latestProjection; + return projection ? { ...projection } : null; +} +function acceptLatestNativeRepairOverride(options) { + const projected = projectNativeRepairHistory(options); + const request = record5(options.override, "Native repair override request"); + exactKeys(request, ["expectedSignatureHash", "summary"], "Native repair override request"); + const expectedSignatureHash = hash4( + request.expectedSignatureHash, + "Native repair override expected signature" + ); + if (typeof request.summary !== "string") { + throw new Error("Native repair override summary is invalid"); + } + if (projected.latestProjection?.disposition === "hard-stop") { + throw new Error("Native repair hard stop cannot be overridden"); + } + if (projected.latestProjection?.disposition !== "manual-stop" || projected.latestProjection.signatureHash !== expectedSignatureHash) { + throw new Error("Native repair override does not match the latest manual stop"); + } + if (projected.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === expectedSignatureHash + )) { + throw new Error("Native repair signature was already overridden"); + } + const overrideSummaryHash = hashNativeRepairOverrideSummary(request.summary); + return { + history: projected.history, + eventProjection: { + signatureHash: expectedSignatureHash, + disposition: "continue", + overrideSummaryHash + } + }; +} +function nativeRepairFailureFacts(input) { + const envelope = parseNativeVerificationEvidenceEnvelope(input.envelope); + const bundle = parseNativeImplementationScopeBundle(input.implementationScope); + if (envelope.result !== "fail") { + throw new Error("Native repair stagnation requires a failed verification envelope"); + } + if (envelope.contractHash !== bundle.scope.contractHash || envelope.implementationScopeHash !== bundle.scope.scopeHash) { + throw new Error("Native repair evidence does not match the implementation scope authority"); + } + const tokens = normalizeNativeRepairFailureTokens({ + categories: input.categories, + failedCheckIds: input.failedCheckIds + }); + return { + contractHash: bundle.scope.contractHash, + implementationScopeHash: nativeRepairScopeHash(bundle), + artifactSnapshotHash: bundle.scope.currentProjectionHash, + categories: tokens.categories, + failedCheckIds: tokens.failedCheckIds + }; +} +function projectionForDecision(decision, overrideSummaryHash) { + return { + signatureHash: decision.signature.signatureHash, + disposition: decision.disposition, + overrideSummaryHash + }; +} +function runtimeContext(input) { + return { + facts: nativeRepairFailureFacts(input), + history: rebuildNativeRepairHistory(input) + }; +} +function inspectNativeRepairFailure(input) { + const context = runtimeContext(input); + const decision = decideNativeRepairStagnation(context); + return { + ...context, + decision, + eventProjection: projectionForDecision(decision, null) + }; +} + +// domains/comet-native/native-runtime-package.ts +var NATIVE_RUNTIME_PACKAGE = { + root: "/comet/native-runtime", + packageKind: "runtime", + definition: { + apiVersion: "comet/v1alpha1", + kind: "Skill", + metadata: { + name: "comet-native-runtime", + version: "3", + description: "Comet-owned state runtime for the Native workflow." + }, + goal: { + statement: "Advance a Native change only after its current guard passes.", + inputs: [], + outputs: [], + success: ["The Native change and Run state agree on the next phase."] + }, + orchestration: { + mode: "deterministic", + entry: "shape", + steps: [ + { id: "shape", action: { type: "checkpoint" }, next: "build" }, + { id: "build", action: { type: "checkpoint" }, next: "verify" }, + { id: "verify", action: { type: "checkpoint" }, next: "archive" }, + { id: "archive", action: { type: "checkpoint" } } + ] + }, + skills: [], + agents: [], + tools: [] + }, + guardrails: { + allowedSkills: [], + allowedAgents: [], + allowedTools: [], + // Native uses the evidence-bound repair episode budget below the engine seam. The generic + // counter remains an action ID source, not a user-visible permanent stop for long-lived changes. + maxIterations: Number.MAX_SAFE_INTEGER, + maxRetriesPerAction: 2, + confirmationRequiredFor: [] + }, + evals: [] +}; +var NATIVE_RUNTIME_HASH = sha256Text("comet-native-runtime:v3:semantic-repair-budget"); +var NATIVE_LEGACY_RUNTIME_IDENTITIES = [ + { + skillVersion: "2", + skillHash: sha256Text("comet-native-runtime:v2:max-iterations-32") + }, + { + skillVersion: "1", + skillHash: sha256Text("comet-native-runtime:v1") + } +]; +function isCompatibleNativeRuntimeIdentity(run) { + return run.skillVersion === NATIVE_RUNTIME_PACKAGE.definition.metadata.version && run.skillHash === NATIVE_RUNTIME_HASH || NATIVE_LEGACY_RUNTIME_IDENTITIES.some( + (identity) => identity.skillVersion === run.skillVersion && identity.skillHash === run.skillHash + ); +} +var nativePhaseResolver = { + resolveStep({ pkg, state }) { + return pkg.definition.orchestration.steps?.find((step) => step.id === state.currentStep); + }, + resolveNext({ step, outcome }) { + if (step.id === "verify" && outcome.state?.verification_result === "fail") return "build"; + return step.next ?? null; + } +}; + +// domains/comet-native/native-trajectory.ts +async function appendNativeTrajectoryEvent(options) { + const trajectory = await readNativeTrajectory(options.changeDir, options.run.trajectoryRef); + const event = { + sequence: trajectory.length + 1, + timestamp: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + type: options.type, + runId: options.run.runId, + data: options.data + }; + await appendNativeTrajectory(options.changeDir, options.run.trajectoryRef, event); + return event; +} +async function writeNativeCheckpoint2(options) { + const checkpoint = { + runId: options.run.runId, + stateVersion: options.run.iteration, + trajectoryOffset: options.trajectoryOffset, + contextHash: null, + artifactsHash: sha256Text(options.evidenceHash), + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString() + }; + await writeNativeCheckpoint(options.changeDir, options.run.checkpointRef, checkpoint); + return checkpoint; +} + +// domains/comet-native/native-transition-evidence.ts +function nativeAdvanceEvidenceHash(evidence) { + return sha256Text( + JSON.stringify({ + summary: evidence.summary, + confirmed: evidence.confirmed ?? false, + artifacts: [...evidence.artifacts ?? []].sort(), + noCodeReason: evidence.noCodeReason ?? null, + verificationResult: evidence.verificationResult ?? null, + verificationReport: evidence.verificationReport ?? null, + verificationReceipt: evidence.verificationReceipt ?? null, + allowPartialScopeHash: evidence.allowPartialScopeHash ?? null, + partialReason: evidence.partialReason ?? null, + repairFailureCategories: [...evidence.repairFailureCategories ?? []].sort(), + repairFailedCheckIds: [...evidence.repairFailedCheckIds ?? []].sort(), + repairOverrideSignature: evidence.repairOverrideSignature ?? null, + repairOverrideSummary: evidence.repairOverrideSummary ?? null + }) + ); +} + +// domains/comet-native/native-transition-journal.ts +var COMMON_JOURNAL_KEYS = [ + "schema", + "id", + "change", + "evidenceHash", + "createdAt", + "previousState", + "nextState", + "previousRun", + "nextRun", + "eventData" +]; +var LEGACY_JOURNAL_KEYS = new Set(COMMON_JOURNAL_KEYS); +var V2_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + ...COMMON_JOURNAL_KEYS, + "minimum_runtime_version", + "revision" +]); +var CURRENT_JOURNAL_KEYS = /* @__PURE__ */ new Set([...V2_JOURNAL_KEYS, "operation"]); +var NATIVE_TRANSITION_JOURNAL_MAX_BYTES = 512 * 1024; +var REQUIRED_TRANSITION_EVENT_DATA_KEYS = /* @__PURE__ */ new Set([ + "previousPhase", + "nextPhase", + "evidenceHash", + "summary", + "artifacts", + "noCodeReason", + "verificationResult" +]); +var TRANSITION_EVENT_DATA_KEYS = /* @__PURE__ */ new Set([ + ...REQUIRED_TRANSITION_EVENT_DATA_KEYS, + "implementationScopeHash", + "repairScopeHash", + "repairStagnation" +]); +var NativeTransitionMigrationRequiredError = class extends Error { + constructor(change) { + super(`Native transition for ${change} requires doctor migration before recovery`); + this.change = change; + this.name = "NativeTransitionMigrationRequiredError"; + } + change; + code = "native-transition-migration-required"; +}; +function nativeTransitionJournalFile(paths, name) { + return path23.join(nativeChangeDir(paths, name), "runtime", "transition.json"); +} +function nativeTransitionLockName(name) { + return `transition-${name}`; +} +async function acquireNativeTransitionLock(paths, name, operation) { + const lockName2 = nativeTransitionLockName(name); + return acquireNativeLock(paths, lockName2, operation); +} +async function withNativeTransitionLock(paths, name, operation, work) { + const lock = await acquireNativeTransitionLock(paths, name, operation); + try { + return await work(); + } finally { + await releaseNativeLock(lock); + } +} +function journalRecord(value) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native transition journal must be an object"); + } + return value; +} +function rejectUnknownJournalFields(journal, known) { + const unknown = Object.keys(journal).find((key) => !known.has(key)); + if (unknown) throw new Error(`Native transition journal contains unknown field: ${unknown}`); +} +function positiveInteger4(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function sameValue(left, right) { + return isDeepStrictEqual(left, right); +} +function parseTransitionEventData(value, evidenceHash) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native transition journal event data is invalid"); + } + const record8 = value; + const keys = Object.keys(record8); + const unknown = keys.find((key) => !TRANSITION_EVENT_DATA_KEYS.has(key)); + const missing = [...REQUIRED_TRANSITION_EVENT_DATA_KEYS].find( + (key) => !Object.hasOwn(record8, key) + ); + const expectedSize = REQUIRED_TRANSITION_EVENT_DATA_KEYS.size + (Object.hasOwn(record8, "implementationScopeHash") ? 1 : 0) + (Object.hasOwn(record8, "repairScopeHash") ? 1 : 0) + (Object.hasOwn(record8, "repairStagnation") ? 1 : 0); + if (unknown || missing || keys.length !== expectedSize) { + throw new Error( + `Native transition journal event data keys are invalid${unknown ? `: ${unknown}` : missing ? `: missing ${missing}` : ""}` + ); + } + if (!["shape", "build", "verify", "archive"].includes(record8.previousPhase) || !["shape", "build", "verify", "archive"].includes(record8.nextPhase)) { + throw new Error("Native transition journal event phases are invalid"); + } + if (record8.evidenceHash !== evidenceHash) { + throw new Error("Native transition journal event evidence hash mismatch"); + } + assertNativeTrajectoryText(record8.summary, "Native transition journal event summary"); + if (redactNativeCredentialText(record8.summary) !== record8.summary) { + throw new Error("Native transition journal event summary contains unredacted credentials"); + } + if (!Array.isArray(record8.artifacts) || record8.artifacts.length > 128 || record8.artifacts.some( + (artifact) => typeof artifact !== "string" || artifact.length === 0 || Buffer.byteLength(artifact, "utf8") > 512 + )) { + throw new Error("Native transition journal event artifacts are invalid"); + } + if (record8.noCodeReason !== null) { + assertNativeTrajectoryText( + record8.noCodeReason, + "Native transition journal event no-code reason" + ); + if (redactNativeCredentialText(record8.noCodeReason) !== record8.noCodeReason) { + throw new Error( + "Native transition journal event no-code reason contains unredacted credentials" + ); + } + } + if (record8.verificationResult !== null && record8.verificationResult !== "pass" && record8.verificationResult !== "fail") { + throw new Error("Native transition journal event verification result is invalid"); + } + const implementationScopeHash = Object.hasOwn(record8, "implementationScopeHash") ? record8.implementationScopeHash : null; + if (implementationScopeHash !== null && (typeof implementationScopeHash !== "string" || !/^[a-f0-9]{64}$/.test(implementationScopeHash) || record8.previousPhase !== "build" && record8.previousPhase !== "verify")) { + throw new Error("Native transition journal implementation scope hash is invalid"); + } + const repairStagnation = Object.hasOwn(record8, "repairStagnation") ? parseNativeRepairTrajectoryProjection(record8.repairStagnation) : null; + if (repairStagnation) { + const failureProjection = repairStagnation.overrideSummaryHash === null; + if (failureProjection && (record8.previousPhase !== "verify" || record8.nextPhase !== "build" || record8.verificationResult !== "fail") || !failureProjection && (record8.previousPhase !== "build" || record8.nextPhase !== "verify" || record8.verificationResult !== null)) { + throw new Error("Native transition journal repair projection does not match its phase"); + } + } + const repairScopeHash = Object.hasOwn(record8, "repairScopeHash") ? record8.repairScopeHash : null; + if (repairScopeHash !== null && (typeof repairScopeHash !== "string" || !/^[a-f0-9]{64}$/.test(repairScopeHash) || record8.previousPhase !== "build" && record8.previousPhase !== "verify" || implementationScopeHash === null)) { + throw new Error("Native transition journal repair scope hash is invalid"); + } + return { + previousPhase: record8.previousPhase, + nextPhase: record8.nextPhase, + evidenceHash, + summary: record8.summary, + artifacts: [...record8.artifacts], + noCodeReason: record8.noCodeReason, + verificationResult: record8.verificationResult, + ...implementationScopeHash ? { implementationScopeHash } : {}, + ...repairScopeHash ? { repairScopeHash } : {}, + ...repairStagnation ? { repairStagnation } : {} + }; +} +function parseLegacyTransitionEventData(value, evidenceHash) { + if (value && typeof value === "object" && !Array.isArray(value)) { + const record8 = value; + const keys = Object.keys(record8); + const legacyRebaseKeys = /* @__PURE__ */ new Set([ + "previousPhase", + "nextPhase", + "evidenceHash", + "summary", + "reason" + ]); + if (keys.length === legacyRebaseKeys.size && keys.every((key) => legacyRebaseKeys.has(key)) && record8.reason === "spec-rebase") { + return parseTransitionEventData( + { + previousPhase: record8.previousPhase, + nextPhase: record8.nextPhase, + evidenceHash: record8.evidenceHash, + summary: record8.summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }, + evidenceHash + ); + } + } + return parseTransitionEventData(value, evidenceHash); +} +function validateJournalEnvelope(journal, expectedName, legacyEventData = false) { + if (journal.change !== expectedName) throw new Error("Native transition journal change mismatch"); + if (typeof journal.id !== "string" || journal.id.length === 0) { + throw new Error("Native transition journal id is invalid"); + } + if (typeof journal.evidenceHash !== "string" || !/^[a-f0-9]{64}$/u.test(journal.evidenceHash)) { + throw new Error("Native transition journal evidence hash is invalid"); + } + if (typeof journal.createdAt !== "string" || Number.isNaN(Date.parse(journal.createdAt))) { + throw new Error("Native transition journal timestamp is invalid"); + } + const eventData = legacyEventData ? parseLegacyTransitionEventData(journal.eventData, journal.evidenceHash) : parseTransitionEventData(journal.eventData, journal.evidenceHash); + const nextRun = parseNativeStoredRunStateValue(journal.nextRun); + const previousRun = journal.previousRun === null ? null : parseNativeStoredRunStateValue(journal.previousRun); + return { + id: journal.id, + evidenceHash: journal.evidenceHash, + createdAt: journal.createdAt, + previousRun, + nextRun, + eventData + }; +} +function assertNativeRunMetadata(run, label, allowCompatibleLegacyIdentity = false) { + if (run.skill !== NATIVE_RUNTIME_PACKAGE.definition.metadata.name || (allowCompatibleLegacyIdentity ? !isCompatibleNativeRuntimeIdentity(run) : run.skillVersion !== NATIVE_RUNTIME_PACKAGE.definition.metadata.version || run.skillHash !== NATIVE_RUNTIME_HASH) || run.orchestration !== NATIVE_RUNTIME_PACKAGE.definition.orchestration.mode || run.pendingRef !== NATIVE_RUN_STORAGE.pendingRef || run.trajectoryRef !== NATIVE_RUN_STORAGE.trajectoryRef || run.contextRef !== NATIVE_RUN_STORAGE.contextRef || run.artifactsRef !== NATIVE_RUN_STORAGE.artifactsRef || run.checkpointRef !== NATIVE_RUN_STORAGE.checkpointRef) { + throw new Error(`Native transition journal ${label} metadata or storage refs are invalid`); + } +} +function runInvariantProjection(run) { + return { + runId: run.runId, + skill: run.skill, + skillVersion: run.skillVersion, + skillHash: run.skillHash, + orchestration: run.orchestration, + pendingRef: run.pendingRef, + trajectoryRef: run.trajectoryRef, + contextRef: run.contextRef, + artifactsRef: run.artifactsRef, + checkpointRef: run.checkpointRef, + retries: run.retries + }; +} +function assertCommittableRun(run, label) { + if (run.status !== "running") { + throw new Error(`Native transition journal ${label} status must be running`); + } + if (run.pending !== null) { + throw new Error(`Native transition journal ${label} pending action must be null`); + } +} +function validateTransitionRunSemantics(previousState, nextState, envelope, allowCompatibleLegacyIdentity = false) { + const { previousRun, nextRun } = envelope; + assertNativeRunMetadata( + nextRun, + "next Run", + allowCompatibleLegacyIdentity || previousRun !== null + ); + assertCommittableRun(nextRun, "next Run"); + if (nextRun.runId !== nextState.run_id || nextRun.currentStep !== nextState.phase) { + throw new Error("Native transition journal next Run does not match the next change state"); + } + if (previousRun === null) { + if (previousState.run_id !== null || previousState.phase !== "shape" || nextRun.iteration !== 1 || Object.keys(nextRun.retries).length !== 0) { + throw new Error("Native transition journal first Run transition is invalid"); + } + return; + } + assertNativeRunMetadata(previousRun, "previous Run", true); + assertCommittableRun(previousRun, "previous Run"); + if (previousState.run_id !== previousRun.runId || previousRun.currentStep !== previousState.phase || nextState.run_id !== previousRun.runId) { + throw new Error("Native transition journal previous Run does not match the change state"); + } + if (nextRun.iteration !== previousRun.iteration + 1) { + throw new Error("Native transition journal next Run iteration must advance exactly once"); + } + if (!isDeepStrictEqual(runInvariantProjection(previousRun), runInvariantProjection(nextRun))) { + throw new Error("Native transition journal Run identity or storage invariants changed"); + } +} +function specRebaseEvidenceHash(name, summary, specChanges) { + return sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: name, + summary, + specChanges + }) + ); +} +function inferredLegacyTransitionOperation(previousState, nextState, event) { + return previousState.phase !== "shape" && nextState.phase === "build" && event.verificationResult === null ? "spec-rebase" : "advance"; +} +function currentStateRefs(state) { + return "implementation_scope" in state ? { + implementation_scope: state.implementation_scope, + verification_evidence: state.verification_evidence, + partial_allowance: state.partial_allowance + } : null; +} +function validateTransitionStateSemantics(previousState, nextState, envelope, operation, legacyOperation = false) { + const event = envelope.eventData; + const previousRefs = currentStateRefs(previousState); + const nextRefs = currentStateRefs(nextState); + if (event.previousPhase !== previousState.phase || event.nextPhase !== nextState.phase) { + throw new Error("Native transition journal event phases do not match its states"); + } + if (previousState.archived || nextState.archived) { + throw new Error("Native transition journal cannot mutate an archived change"); + } + if (operation === "evidence-retreat") { + const sameIdentity2 = previousState.name === nextState.name && previousState.language === nextState.language && previousState.brief === nextState.brief && previousState.approval === nextState.approval && ("approved_contract_hash" in previousState ? "approved_contract_hash" in nextState && previousState.approved_contract_hash === nextState.approved_contract_hash : !("approved_contract_hash" in nextState)) && previousState.created_at === nextState.created_at && previousState.run_id === nextState.run_id && isDeepStrictEqual(previousState.spec_changes, nextState.spec_changes); + const expectedHash = nativeAdvanceEvidenceHash({ summary: event.summary }); + const validSourcePhase = previousState.phase === "archive" && previousState.verification_result === "pass" || previousState.phase === "verify" && previousState.verification_result === "pending"; + if (!validSourcePhase || nextState.phase !== "build" || nextState.verification_result !== "pending" || nextState.verification_report !== null || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || envelope.evidenceHash !== expectedHash || !sameIdentity2 || previousRefs === null || nextRefs === null || nextRefs.implementation_scope !== null || nextRefs.verification_evidence !== null || nextRefs.partial_allowance !== null) { + throw new Error("Native transition journal evidence retreat semantics are invalid"); + } + return; + } + if (operation === "spec-rebase") { + const currentHash = specRebaseEvidenceHash( + previousState.name, + event.summary, + nextState.spec_changes + ); + const legacyHash = sha256Text(`spec-rebase:${previousState.name}:${event.summary}`); + if (previousState.phase === "shape" || nextState.phase !== "build" || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || nextState.verification_result !== "pending" || nextState.verification_report !== null || !legacyOperation && envelope.evidenceHash !== currentHash || legacyOperation && envelope.evidenceHash !== currentHash && envelope.evidenceHash !== legacyHash || "implementation_scope" in nextState && (nextState.implementation_scope !== null || nextState.verification_evidence !== null || nextState.partial_allowance !== null)) { + throw new Error("Native transition journal spec rebase semantics are invalid"); + } + return; + } + if (previousState.phase === "shape") { + if (nextState.phase !== "build" || event.verificationResult !== null || event.artifacts.length !== 0 || event.noCodeReason !== null || previousState.verification_result !== "pending" || nextState.verification_result !== "pending" || previousState.verification_report !== null || nextState.verification_report !== null || previousRefs !== null && nextRefs !== null && (previousRefs.implementation_scope !== null || previousRefs.verification_evidence !== null || previousRefs.partial_allowance !== null || nextRefs.implementation_scope !== null || nextRefs.verification_evidence !== null || nextRefs.partial_allowance !== null)) { + throw new Error("Native transition journal Shape to Build semantics are invalid"); + } + return; + } + if (previousState.phase === "build") { + if (nextState.phase !== "verify" || event.verificationResult !== null || event.artifacts.length === 0 && event.noCodeReason === null || nextState.verification_result !== "pending" || nextState.verification_report !== null || previousRefs !== null && nextRefs !== null && (nextRefs.implementation_scope === null || nextRefs.verification_evidence !== null || nextRefs.implementation_scope !== previousRefs.implementation_scope && nextRefs.partial_allowance !== null && nextRefs.partial_allowance === previousRefs.partial_allowance)) { + throw new Error("Native transition journal Build to Verify semantics are invalid"); + } + return; + } + if (previousState.phase === "verify") { + const expectedNext = event.verificationResult === "pass" ? "archive" : "build"; + if (event.verificationResult !== "pass" && event.verificationResult !== "fail" || nextState.phase !== expectedNext || event.artifacts.length !== 0 || event.noCodeReason !== null || nextState.verification_result !== event.verificationResult || nextState.verification_report === null || previousRefs !== null && nextRefs !== null && (previousRefs.implementation_scope === null || nextRefs.implementation_scope !== previousRefs.implementation_scope || nextRefs.partial_allowance !== previousRefs.partial_allowance || previousRefs.verification_evidence !== null || nextRefs.verification_evidence === null)) { + throw new Error("Native transition journal Verify outcome semantics are invalid"); + } + return; + } + throw new Error("Native transition journal cannot advance from Archive"); +} +function parseNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, CURRENT_JOURNAL_KEYS); + if (journal.schema !== NATIVE_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_TRANSITION_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger4( + journal.minimum_runtime_version, + "Native transition minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native transition requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native transition ${NATIVE_TRANSITION_SCHEMA} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger4(journal.revision, "Native transition revision"); + if (revision !== 1) throw new Error("Native transition journal revision must be 1"); + if (journal.operation !== "advance" && journal.operation !== "spec-rebase" && journal.operation !== "evidence-retreat") { + throw new Error("Native transition journal operation is invalid"); + } + const envelope = validateJournalEnvelope(journal, expectedName); + const previousState = parseNativeChangeValue(journal.previousState); + const nextState = parseNativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native transition journal state mismatch"); + } + validateTransitionStateSemantics(previousState, nextState, envelope, journal.operation); + validateTransitionRunSemantics(previousState, nextState, envelope); + if (nextState.revision !== previousState.revision + 1) { + throw new Error("Native transition journal state revision must advance exactly once"); + } + return { + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + operation: journal.operation, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function parseLegacyNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, LEGACY_JOURNAL_KEYS); + if (journal.schema !== NATIVE_LEGACY_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_LEGACY_TRANSITION_SCHEMA}`); + } + const envelope = validateJournalEnvelope(journal, expectedName, true); + const previousState = parseLegacyNativeChangeValue(journal.previousState); + const nextState = parseLegacyNativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native transition journal state mismatch"); + } + validateTransitionStateSemantics( + previousState, + nextState, + envelope, + inferredLegacyTransitionOperation(previousState, nextState, envelope.eventData), + true + ); + validateTransitionRunSemantics(previousState, nextState, envelope, true); + return { + schema: NATIVE_LEGACY_TRANSITION_SCHEMA, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function parseV2NativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + rejectUnknownJournalFields(journal, V2_JOURNAL_KEYS); + if (journal.schema !== NATIVE_V2_TRANSITION_SCHEMA) { + throw new Error(`Expected Native transition schema ${NATIVE_V2_TRANSITION_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger4( + journal.minimum_runtime_version, + "Native v2 transition minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error( + `Native transition ${NATIVE_V2_TRANSITION_SCHEMA} minimum_runtime_version must be 2` + ); + } + const revision = positiveInteger4(journal.revision, "Native v2 transition revision"); + if (revision !== 1) throw new Error("Native v2 transition journal revision must be 1"); + const envelope = validateJournalEnvelope(journal, expectedName, true); + const previousState = parseV2NativeChangeValue(journal.previousState); + const nextState = parseV2NativeChangeValue(journal.nextState); + if (previousState.name !== expectedName || nextState.name !== expectedName) { + throw new Error("Native v2 transition journal state mismatch"); + } + validateTransitionStateSemantics( + previousState, + nextState, + envelope, + inferredLegacyTransitionOperation(previousState, nextState, envelope.eventData), + true + ); + validateTransitionRunSemantics(previousState, nextState, envelope, true); + if (nextState.revision !== previousState.revision + 1) { + throw new Error("Native v2 transition journal state revision must advance exactly once"); + } + return { + schema: NATIVE_V2_TRANSITION_SCHEMA, + minimum_runtime_version: 2, + revision, + id: envelope.id, + change: expectedName, + evidenceHash: envelope.evidenceHash, + createdAt: envelope.createdAt, + previousState, + nextState, + previousRun: envelope.previousRun, + nextRun: envelope.nextRun, + eventData: envelope.eventData + }; +} +function inspectNativeTransitionJournalValue(value, expectedName) { + const journal = journalRecord(value); + if (journal.schema === NATIVE_TRANSITION_SCHEMA) { + return { status: "current", journal: parseNativeTransitionJournalValue(journal, expectedName) }; + } + if (journal.schema === NATIVE_LEGACY_TRANSITION_SCHEMA) { + return { + status: "migration-required", + journal: parseLegacyNativeTransitionJournalValue(journal, expectedName) + }; + } + if (journal.schema === NATIVE_V2_TRANSITION_SCHEMA) { + return { + status: "migration-required", + journal: parseV2NativeTransitionJournalValue(journal, expectedName) + }; + } + throw new Error(`Unsupported Native transition journal schema: ${String(journal.schema)}`); +} +async function inspectPendingNativeTransitionSchema(paths, name) { + const file = nativeTransitionJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file, + maxBytes: NATIVE_TRANSITION_JOURNAL_MAX_BYTES, + label: `Native transition journal ${name}` + }); + return inspectNativeTransitionJournalValue(JSON.parse(snapshot2.bytes.toString("utf8")), name); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function inspectPendingNativeTransition(paths, name) { + const inspection = await inspectPendingNativeTransitionSchema(paths, name); + if (!inspection) return null; + if (inspection.status === "migration-required") { + throw new NativeTransitionMigrationRequiredError(name); + } + return inspection.journal; +} +async function prepareNativeTransition(options) { + if (await hasPendingNativeSchemaMigration(options.paths, options.nextState.name)) { + throw new Error( + `Native schema migration is incomplete for ${options.nextState.name}; run doctor --repair` + ); + } + await assertNativeTrajectoryHealthy(options.paths, options.nextState.name); + const journal = { + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + operation: options.operation ?? "advance", + id: options.transitionId?.() ?? randomUUID3(), + change: options.nextState.name, + evidenceHash: options.evidenceHash, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString(), + previousState: options.previousState, + nextState: options.nextState, + previousRun: options.previousRun, + nextRun: options.nextRun, + eventData: options.eventData + }; + const validated = parseNativeTransitionJournalValue(journal, journal.change); + const file = nativeTransitionJournalFile(options.paths, validated.change); + await resolveContainedNativePath(options.paths.nativeRoot, file); + if (await inspectPendingNativeTransition(options.paths, validated.change)) { + throw new Error(`Native transition recovery is already pending for ${validated.change}`); + } + await atomicWriteJson(file, validated); + return validated; +} +function assertRunRecoverySource(actual, journal) { + if (sameValue(actual, journal.nextRun)) return "next"; + if (journal.previousRun === null) { + if (actual === null) return "previous"; + } else if (sameValue(actual, journal.previousRun)) { + return "previous"; + } + throw new Error( + `Native transition Run content changed for ${journal.change}; recovery journal was preserved` + ); +} +function assertChangeRecoverySource(actual, journal) { + if (sameValue(actual, journal.nextState)) return "next"; + if (sameValue(actual, journal.previousState)) return "previous"; + throw new Error( + `Native transition change content changed for ${journal.change}; recovery journal was preserved` + ); +} +function expectedTrajectoryEvent(options) { + const { journal } = options; + return trajectoryEventForComparison({ + sequence: options.sequence, + timestamp: journal.createdAt, + type: options.type, + runId: journal.nextRun.runId, + data: options.type === "run_started" ? { + runtime: "comet-native", + phase: journal.previousState.phase, + transitionId: journal.id + } : { ...journal.eventData, transitionId: journal.id } + }); +} +function trajectoryEventForComparison(event) { + return { + sequence: event.sequence, + timestamp: event.timestamp, + type: event.type, + runId: event.runId, + data: event.data + }; +} +function inspectExistingTransitionEvents(trajectory, journal) { + const collisions = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.transitionId === journal.id); + const started = collisions.filter(({ event }) => event.type === "run_started"); + const transitioned = collisions.filter(({ event }) => event.type === "state_transitioned"); + if (collisions.length !== started.length + transitioned.length || started.length > (journal.previousRun === null ? 1 : 0) || transitioned.length > 1 || journal.previousRun === null && transitioned.length === 1 && (started.length !== 1 || started[0].index >= transitioned[0].index)) { + throw new Error( + `Native trajectory transition id collision for ${journal.change}; recovery journal was preserved` + ); + } + for (const item of [...started, ...transitioned]) { + const expected = expectedTrajectoryEvent({ + sequence: item.index + 1, + journal, + type: item.event.type + }); + if (!sameValue(trajectoryEventForComparison(item.event), expected)) { + throw new Error( + `Native trajectory event changed for transition ${journal.id}; recovery journal was preserved` + ); + } + } + return { + started: started[0]?.event ?? null, + transitioned: transitioned[0]?.event ?? null + }; +} +async function continueNativeTransitionLocked(paths, name, hooks) { + if (await hasPendingNativeSchemaMigration(paths, name)) { + throw new Error(`Native schema migration is incomplete for ${name}; run doctor --repair`); + } + await assertNativeTrajectoryHealthy(paths, name); + const journal = await inspectPendingNativeTransition(paths, name); + if (!journal) return null; + const changeDir = nativeChangeDir(paths, name); + const initialEvents = inspectExistingTransitionEvents( + await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef), + journal + ); + const [actualRun, actualChange] = await Promise.all([ + readNativeRunState(changeDir), + readNativeChange(paths, name) + ]); + const runSource = assertRunRecoverySource(actualRun, journal); + const changeSource = assertChangeRecoverySource(actualChange, journal); + if ((initialEvents.started || initialEvents.transitioned) && (runSource !== "next" || changeSource !== "next")) { + throw new Error( + `Native trajectory is ahead of transition state for ${journal.change}; recovery journal was preserved` + ); + } + if (runSource === "previous") { + await writeNativeRunState(changeDir, journal.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + if (!sameValue(await readNativeRunState(changeDir), journal.nextRun)) { + throw new Error( + `Native transition Run content changed while continuing ${journal.change}; recovery journal was preserved` + ); + } + if (changeSource === "previous") { + const persisted = await compareAndSwapNativeChangeLocked( + paths, + journal.nextState, + journal.previousState.revision + ); + if (!sameValue(persisted, journal.nextState)) { + throw new Error( + `Native transition change write diverged for ${journal.change}; recovery journal was preserved` + ); + } + await hooks?.afterChangeStateWritten?.(journal); + } + if (!sameValue(await readNativeChange(paths, name), journal.nextState)) { + throw new Error( + `Native transition change content changed while continuing ${journal.change}; recovery journal was preserved` + ); + } + const activeJournal = await inspectPendingNativeTransition(paths, name); + if (!activeJournal || !sameValue(activeJournal, journal)) { + throw new Error( + `Native transition journal changed while continuing ${journal.change}; it was preserved` + ); + } + const existingEvents = inspectExistingTransitionEvents( + await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef), + journal + ); + if (journal.previousRun === null) { + if (!existingEvents.started) { + await appendNativeTrajectoryEvent({ + changeDir, + run: journal.nextRun, + type: "run_started", + data: { + runtime: "comet-native", + phase: journal.previousState.phase, + transitionId: journal.id + }, + now: new Date(journal.createdAt) + }); + } + } + let event = existingEvents.transitioned; + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir, + run: journal.nextRun, + type: "state_transitioned", + data: { ...journal.eventData, transitionId: journal.id }, + now: new Date(journal.createdAt) + }); + } + await writeNativeCheckpoint2({ + changeDir, + run: journal.nextRun, + trajectoryOffset: event.sequence, + evidenceHash: journal.evidenceHash, + now: new Date(journal.createdAt) + }); + const [finalRun, finalChange] = await Promise.all([ + readNativeRunState(changeDir), + readNativeChange(paths, name) + ]); + assertRunRecoverySource(finalRun, { ...journal, previousRun: journal.nextRun }); + assertChangeRecoverySource(finalChange, { + ...journal, + previousState: journal.nextState + }); + const finalJournal = await inspectPendingNativeTransition(paths, name); + if (!finalJournal || !sameValue(finalJournal, journal)) { + throw new Error( + `Native transition journal changed while continuing ${journal.change}; it was not removed` + ); + } + await fs16.rm(nativeTransitionJournalFile(paths, name), { force: true }); + return journal.nextState; +} +async function continueNativeTransition(paths, name, hooks) { + return withNativeMutationLock( + paths, + `continue transition ${name}`, + () => withNativeTransitionLock( + paths, + name, + `continue transition ${name}`, + () => continueNativeTransitionLocked(paths, name, hooks) + ) + ); +} + +// domains/comet-native/native-verification-runtime.ts +import path28 from "node:path"; + +// domains/comet-native/native-acceptance.ts +import path24 from "node:path"; +var ACCEPTANCE_HASH_TAG = "comet.native.acceptance.v1"; +var ACCEPTANCE_ID_PATTERN = /^acceptance-[a-f0-9]{64}$/u; +var EVIDENCE_ENTRY_KEYS = /* @__PURE__ */ new Set(["acceptance_id", "evidence_refs", "skipped_reason"]); +var ACCEPTANCE_HASH_PATTERN = /^[a-f0-9]{64}$/u; +var ACCEPTANCE_CURSOR_PATTERN = /^native-acceptance-v1\.([a-f0-9]{64})\.([0-9a-z]+)\.([a-f0-9]{64})$/u; +var NATIVE_ACCEPTANCE_PAGE_LIMITS = Object.freeze({ + maxItems: 16, + maxTextBytes: 512, + maxContextItems: 4, + maxContextItemBytes: 256, + maxSerializedBytes: 32 * 1024 +}); +var NATIVE_ACCEPTANCE_LIMITS = Object.freeze({ + maxCriteria: 1024 +}); +var NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER = ""; +var NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER = ""; +function truncateUtf8(value, maxBytes) { + if (Buffer.byteLength(value, "utf8") <= maxBytes) return { value, truncated: false }; + let result2 = ""; + let bytes = 0; + for (const character of value) { + const characterBytes = Buffer.byteLength(character, "utf8"); + if (bytes + characterBytes > maxBytes) break; + result2 += character; + bytes += characterBytes; + } + return { value: result2, truncated: true }; +} +function acceptanceCursor(acceptanceHash, offset) { + const encodedOffset = offset.toString(36); + const cursorHash = canonicalHash("comet.native.acceptance-cursor.v1", { + acceptanceHash, + offset + }); + return `native-acceptance-v1.${acceptanceHash}.${encodedOffset}.${cursorHash}`; +} +function acceptanceOffset(options) { + if (!ACCEPTANCE_HASH_PATTERN.test(options.acceptanceHash)) { + throw new Error("Native acceptance page hash is invalid"); + } + if (options.cursor === void 0 || options.cursor === null) return 0; + const match = ACCEPTANCE_CURSOR_PATTERN.exec(options.cursor); + if (!match) throw new Error("Native acceptance cursor is invalid"); + if (match[1] !== options.acceptanceHash) { + throw new Error("Native acceptance cursor is stale"); + } + const offset = Number.parseInt(match[2], 36); + if (!Number.isSafeInteger(offset) || offset <= 0 || offset >= options.total || offset.toString(36) !== match[2]) { + throw new Error("Native acceptance cursor offset is invalid"); + } + if (match[3] !== canonicalHash("comet.native.acceptance-cursor.v1", { + acceptanceHash: options.acceptanceHash, + offset + })) { + throw new Error("Native acceptance cursor integrity check failed"); + } + return offset; +} +function acceptanceProjection(criterion2) { + const text = truncateUtf8(criterion2.text, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxTextBytes); + const projectedContext = criterion2.context.slice(0, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxContextItems).map((entry2) => truncateUtf8(entry2, NATIVE_ACCEPTANCE_PAGE_LIMITS.maxContextItemBytes)); + return { + id: criterion2.id, + kind: criterion2.kind, + source: criterion2.source, + context: projectedContext.map((entry2) => entry2.value), + text: text.value, + contextTruncated: criterion2.context.length > projectedContext.length || projectedContext.some((entry2) => entry2.truncated), + textTruncated: text.truncated + }; +} +function projectNativeAcceptancePage(options) { + const offset = acceptanceOffset({ + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + cursor: options.cursor + }); + const items = []; + const remaining = options.criteria.slice(offset, offset + NATIVE_ACCEPTANCE_PAGE_LIMITS.maxItems); + for (const criterion2 of remaining) { + const candidate = [...items, acceptanceProjection(criterion2)]; + const nextOffset2 = offset + candidate.length; + const trial = { + schema: "comet.native.acceptance-page.v1", + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + offset, + items: candidate, + nextCursor: nextOffset2 < options.criteria.length ? acceptanceCursor(options.acceptanceHash, nextOffset2) : null, + limits: { ...NATIVE_ACCEPTANCE_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(trial), "utf8") > NATIVE_ACCEPTANCE_PAGE_LIMITS.maxSerializedBytes) { + if (items.length === 0) { + throw new Error("Native acceptance criterion exceeds the page serialization budget"); + } + break; + } + items.push(candidate.at(-1)); + } + if (options.criteria.length > 0 && items.length === 0) { + throw new Error("Native acceptance page could not project its next criterion"); + } + const nextOffset = offset + items.length; + const page = { + schema: "comet.native.acceptance-page.v1", + acceptanceHash: options.acceptanceHash, + total: options.criteria.length, + offset, + items, + nextCursor: nextOffset < options.criteria.length ? acceptanceCursor(options.acceptanceHash, nextOffset) : null, + limits: { ...NATIVE_ACCEPTANCE_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(page), "utf8") > NATIVE_ACCEPTANCE_PAGE_LIMITS.maxSerializedBytes) { + throw new Error("Native acceptance page exceeds its serialization budget"); + } + return page; +} +function markdownHeading(line) { + const match = /^ {0,3}(#{1,6})[ \t]+(.+?)[ \t]*$/u.exec(line); + if (!match) return null; + return { + level: match[1].length, + text: match[2].replace(/[ \t]+#+[ \t]*$/u, "").trim() + }; +} +function nextFenceState(line, current) { + const match = /^ {0,3}(`{3,}|~{3,})(.*)$/u.exec(line); + if (!match) return current; + const marker = match[1][0]; + if (current === null) return { marker, length: match[1].length }; + if (marker === current.marker && match[1].length >= current.length && match[2].trim().length === 0) { + return null; + } + return current; +} +function* iterateScannedMarkdown(markdown) { + let fence = null; + let htmlComment = false; + let htmlTag = null; + const normalized = markdown.replace(/\r\n?/gu, "\n"); + let start = 0; + let index = 0; + while (start <= normalized.length) { + const end = normalized.indexOf("\n", start); + const line = end === -1 ? normalized.slice(start) : normalized.slice(start, end); + const body = fence === null && !htmlComment && htmlTag === null; + yield { line, body, index }; + index += 1; + if (fence !== null) { + fence = nextFenceState(line, fence); + } else if (htmlComment) { + if (line.includes("-->")) htmlComment = false; + } else if (htmlTag !== null) { + if (new RegExp(``, "iu").test(line)) htmlTag = null; + } else { + const nextFence = nextFenceState(line, null); + if (nextFence !== null) { + fence = nextFence; + } else { + const trimmed = line.trimStart(); + if (trimmed.startsWith("")) { + htmlComment = true; + } else { + const htmlStart = /^<([A-Za-z][A-Za-z0-9-]*)\b[^>]*>/u.exec(trimmed); + if (htmlStart && !trimmed.startsWith("") && !new RegExp(``, "iu").test(trimmed)) { + htmlTag = htmlStart[1]; + } + } + } + } + if (end === -1) break; + start = end + 1; + } +} +function scanMarkdown(markdown) { + return [...iterateScannedMarkdown(markdown)].map(({ line, body }) => ({ line, body })); +} +function normalizeNativeAcceptanceText(value) { + return value.normalize("NFC").replace(/\s+/gu, " ").trim(); +} +function hasControlCharacter2(value) { + return Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }); +} +function criterion(kind, source, rawText, rawContext = []) { + const text = normalizeNativeAcceptanceText(rawText); + const normalizedSource = source.replaceAll("\\", "/").trim(); + const context = rawContext.map(normalizeNativeAcceptanceText); + if (text.length === 0) throw new Error(`${kind} acceptance criterion must not be empty`); + if (normalizedSource.length === 0) { + throw new Error(`${kind} acceptance criterion source must not be empty`); + } + return { + id: `acceptance-${canonicalHash(ACCEPTANCE_HASH_TAG, { + kind, + source: normalizedSource, + context, + text + })}`, + kind, + source: normalizedSource, + context, + text + }; +} +function uniqueCriteria(criteria, label) { + const seen = /* @__PURE__ */ new Set(); + for (const item of criteria) { + if (seen.has(item.id)) throw new Error(`${label} contains duplicate acceptance criteria`); + seen.add(item.id); + } + return criteria; +} +function acceptanceSectionBounds(markdown) { + let sectionStart = null; + let sectionEnd = null; + let matches = 0; + let lineCount = 0; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + lineCount = index + 1; + const heading = body ? markdownHeading(line) : null; + if (heading?.level !== 1) continue; + if (heading.text.toLocaleLowerCase("en-US") === "acceptance examples") { + matches += 1; + if (matches === 1) sectionStart = index + 1; + } else if (sectionStart !== null && sectionEnd === null) { + sectionEnd = index; + } + } + if (matches === 0 || sectionStart === null) return null; + if (matches !== 1) { + throw new Error("Brief must contain exactly one Acceptance examples section"); + } + return { start: sectionStart, end: sectionEnd ?? lineCount }; +} +function deriveBriefAcceptanceCriteria(markdown, source = "brief.md", maxCriteria = NATIVE_ACCEPTANCE_LIMITS.maxCriteria) { + if (!Number.isSafeInteger(maxCriteria) || maxCriteria < 0) { + throw new Error("Native brief acceptance budget is invalid"); + } + const section = acceptanceSectionBounds(markdown); + if (section === null) return []; + let topLevelIndent = null; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + if (index < section.start || index >= section.end) continue; + const listItem = body ? /^( {0,3})[-*+][ \t]+/u.exec(line) : null; + if (listItem === null) continue; + const indent = listItem[1].length; + topLevelIndent = topLevelIndent === null ? indent : Math.min(topLevelIndent, indent); + } + const items = []; + let active = null; + const pushActive = () => { + if (active === null) return; + if (items.length >= maxCriteria) { + throw new Error(`Native acceptance exceeds its ${maxCriteria}-criterion acceptance budget`); + } + items.push(active); + }; + for (const { line, body, index } of iterateScannedMarkdown(markdown)) { + if (index < section.start || index >= section.end) continue; + const listItem = body ? /^( {0,3})[-*+][ \t]+(.*)$/u.exec(line) : null; + if (listItem && listItem[1].length === topLevelIndent) { + pushActive(); + active = [listItem[2]]; + } else if (active !== null) { + active.push(line); + } + } + pushActive(); + return uniqueCriteria( + items.map((lines) => criterion("brief-example", source, lines.join("\n"))), + "Brief" + ); +} +function deriveSpecAcceptanceCriteria(markdown, source = "spec.md", maxCriteria = NATIVE_ACCEPTANCE_LIMITS.maxCriteria) { + if (!Number.isSafeInteger(maxCriteria) || maxCriteria < 0) { + throw new Error("Native specification acceptance budget is invalid"); + } + const criteria = []; + const ancestry = []; + let active = null; + const flush = () => { + if (active === null) return; + if (criteria.length >= maxCriteria) { + throw new Error(`Native acceptance exceeds its ${maxCriteria}-criterion acceptance budget`); + } + criteria.push( + criterion("spec-scenario", source, [active.title, ...active.body].join("\n"), active.context) + ); + active = null; + }; + for (const { line, body } of iterateScannedMarkdown(markdown)) { + const heading = body ? markdownHeading(line) : null; + const scenario = heading ? /^Scenario\s*:\s*(.*)$/iu.exec(heading.text) : null; + if (scenario) { + flush(); + while (ancestry.at(-1) && ancestry.at(-1).level >= heading.level) ancestry.pop(); + const title = normalizeNativeAcceptanceText(scenario[1]); + if (title.length === 0) throw new Error("Scenario title must not be empty"); + active = { + level: heading.level, + title, + body: [], + context: ancestry.map((item) => item.text) + }; + } else if (heading) { + if (active !== null && heading.level <= active.level) flush(); + else if (active !== null) active.body.push(line); + while (ancestry.at(-1) && ancestry.at(-1).level >= heading.level) ancestry.pop(); + ancestry.push(heading); + } else if (active !== null && body) { + active.body.push(line); + } + } + flush(); + return uniqueCriteria(criteria, "Specification"); +} +function normalizeEvidenceRef(value, acceptanceId) { + const normalized = value.trim().replaceAll("\\", "/"); + if (normalized.length === 0 || hasControlCharacter2(normalized) || path24.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~|[A-Za-z][A-Za-z0-9+.-]*:)/u.test(normalized) || normalized.split("/").includes("..")) { + throw new Error(`Acceptance evidence ${acceptanceId} has an unsafe evidence ref`); + } + const portable = path24.posix.normalize(normalized); + if (portable === "." || portable === ".." || portable.startsWith("../")) { + throw new Error(`Acceptance evidence ${acceptanceId} has an unsafe evidence ref`); + } + if (portable.split("/").some( + (segment) => segment.toLowerCase() === ".git" || segment.toLowerCase().startsWith(".env") + )) { + throw new Error(`Acceptance evidence ${acceptanceId} references sensitive content`); + } + return portable; +} +function evidenceRecord2(value, index) { + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Acceptance evidence entry ${index} must be an object`); + } + const record8 = value; + const unknown = Object.keys(record8).filter((key) => !EVIDENCE_ENTRY_KEYS.has(key)); + if (unknown.length > 0) { + throw new Error( + `Acceptance evidence entry ${index} has unknown field(s): ${unknown.join(", ")}` + ); + } + return record8; +} +function validateEvidenceEntries(value) { + if (!Array.isArray(value)) + throw new Error("Native acceptance evidence block must be a JSON array"); + const seenIds = /* @__PURE__ */ new Set(); + return value.map((item, index) => { + const record8 = evidenceRecord2(item, index); + const acceptanceId = record8.acceptance_id; + if (typeof acceptanceId !== "string" || !ACCEPTANCE_ID_PATTERN.test(acceptanceId)) { + throw new Error(`Acceptance evidence entry ${index} has an invalid acceptance_id`); + } + if (seenIds.has(acceptanceId)) { + throw new Error(`Native acceptance evidence has duplicate acceptance_id: ${acceptanceId}`); + } + seenIds.add(acceptanceId); + if (!Array.isArray(record8.evidence_refs)) { + throw new Error(`Acceptance evidence ${acceptanceId} requires an evidence_refs array`); + } + const evidenceRefs = record8.evidence_refs.map((reference) => { + if (typeof reference !== "string" || reference.trim().length === 0) { + throw new Error(`Acceptance evidence ${acceptanceId} has a non-empty string requirement`); + } + return normalizeEvidenceRef(reference, acceptanceId); + }); + if (new Set(evidenceRefs).size !== evidenceRefs.length) { + throw new Error(`Acceptance evidence ${acceptanceId} has a duplicate evidence ref`); + } + let skippedReason; + if (Object.prototype.hasOwnProperty.call(record8, "skipped_reason")) { + if (typeof record8.skipped_reason !== "string" || record8.skipped_reason.trim().length === 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} skipped_reason must be a non-empty string` + ); + } + skippedReason = record8.skipped_reason.trim(); + } + if (evidenceRefs.length === 0 && skippedReason === void 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} requires evidence_refs or skipped_reason` + ); + } + if (evidenceRefs.length > 0 && skippedReason !== void 0) { + throw new Error( + `Acceptance evidence ${acceptanceId} must not include both evidence and a skip` + ); + } + return { + acceptance_id: acceptanceId, + evidence_refs: evidenceRefs, + ...skippedReason === void 0 ? {} : { skipped_reason: skippedReason } + }; + }); +} +function parseNativeVerificationMachineBlock(markdown) { + const lines = scanMarkdown(markdown); + const invalidContextMarker = lines.some( + ({ line, body }) => !body && (line === NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER || line === NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER) + ); + if (invalidContextMarker) { + throw new Error("Native acceptance evidence markers must be in the Markdown body"); + } + const starts = lines.flatMap( + ({ line, body }, index) => body && line === NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER ? [index] : [] + ); + const ends = lines.flatMap( + ({ line, body }, index) => body && line === NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER ? [index] : [] + ); + if (starts.length !== 1 || ends.length !== 1) { + throw new Error("Verification must contain exactly one Native acceptance evidence block"); + } + if (starts[0] >= ends[0]) { + throw new Error("Native acceptance evidence markers are out of order"); + } + const payload = lines.slice(starts[0] + 1, ends[0]).map(({ line }) => line).join("\n").trim(); + if (payload.length === 0) throw new Error("Native acceptance evidence block is empty"); + let parsed; + try { + parsed = JSON.parse(payload); + } catch (error) { + throw new Error( + `Native acceptance evidence block is invalid JSON: ${error.message}`, + { cause: error } + ); + } + const validated = validateEvidenceEntries(parsed); + const canonicalPayload = canonicalEvidencePayload(validated); + if (payload !== canonicalPayload) { + throw new Error("Native acceptance evidence block must use canonical serialization"); + } + return validated; +} +function canonicalEvidencePayload(entries) { + const validated = validateEvidenceEntries([...entries]).map((entry2) => ({ ...entry2, evidence_refs: [...entry2.evidence_refs].sort() })).sort((left, right) => left.acceptance_id.localeCompare(right.acceptance_id)); + return JSON.stringify(validated, null, 2); +} +function serializeNativeVerificationMachineBlock(entries) { + return [ + NATIVE_ACCEPTANCE_EVIDENCE_START_MARKER, + canonicalEvidencePayload(entries), + NATIVE_ACCEPTANCE_EVIDENCE_END_MARKER + ].join("\n"); +} + +// domains/comet-native/native-check-receipt-storage.ts +import { constants as fsConstants4, promises as fs17 } from "node:fs"; +import path26 from "node:path"; + +// domains/comet-native/native-check-receipt-model.ts +import path25 from "node:path"; +var NATIVE_CHECK_RECEIPT_SCHEMA = "comet.native.check-receipt.v1"; +var NATIVE_CHECK_RECEIPT_HASH_TAG = "comet.native.check-receipt.v1"; +var NATIVE_CHECK_POLICY = "scoped-text-safety"; +var NATIVE_CHECK_POLICY_VERSION = 1; +var NATIVE_CHECK_LIMITS = Object.freeze({ + maxFiles: 256, + maxFileBytes: 1024 * 1024, + maxTotalBytes: 8 * 1024 * 1024, + maxIssues: 128 +}); +var HASH_PATTERN10 = /^[a-f0-9]{64}$/u; +var CHANGE_NAME_PATTERN3 = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CHECKER_HASH_TAG = "comet.native.checker-policy.v1"; +var CHECK_INPUT_HASH_TAG = "comet.native.check-input.v1"; +var MAX_ISSUE_PATH_BYTES = 2048; +var NATIVE_CHECKER_HASH = canonicalHash(CHECKER_HASH_TAG, { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + limits: NATIVE_CHECK_LIMITS, + checks: ["conflict-marker", "space-before-tab", "trailing-whitespace"], + binaryHandling: "skip-and-count" +}); +var ISSUE_KINDS = /* @__PURE__ */ new Set([ + "conflict-marker", + "trailing-whitespace", + "space-before-tab", + "scope-mismatch", + "unsafe-file", + "scan-limit" +]); +var ISSUE_KIND_ORDER = [ + "conflict-marker", + "trailing-whitespace", + "space-before-tab", + "scope-mismatch", + "unsafe-file", + "scan-limit" +]; +var STALE_REASON_ORDER = [ + "contract-before-does-not-match-scope", + "implementation-before-does-not-match-scope", + "contract-changed-during-check", + "implementation-changed-during-check", + "contract-after-does-not-match-scope", + "implementation-after-does-not-match-scope" +]; +var STALE_REASONS = new Set(STALE_REASON_ORDER); +function record6(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactKeys2(value, expected, label) { + const keys = new Set(expected); + const unknown = Object.keys(value).filter((key) => !keys.has(key)); + const missing = expected.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function hash5(value, label) { + if (typeof value !== "string" || !HASH_PATTERN10.test(value)) { + throw new Error(`${label} must be a SHA-256 hash`); + } + return value; +} +function nonNegativeInteger2(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function positiveInteger5(value, label) { + const parsed = nonNegativeInteger2(value, label); + if (parsed < 1) throw new Error(`${label} must be a positive integer`); + return parsed; +} +function isoTimestamp2(value, label) { + if (typeof value !== "string") throw new Error(`${label} must be an ISO timestamp`); + const parsed = new Date(value); + if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) { + throw new Error(`${label} must be a canonical ISO timestamp`); + } + return value; +} +function projectRelativePath2(value, label) { + if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > MAX_ISSUE_PATH_BYTES || value.includes("\\") || value.includes("\0") || value.endsWith("/") || /^[a-zA-Z]:/u.test(value)) { + throw new Error(`${label} must be a bounded normalized project-relative path`); + } + const normalized = path25.posix.normalize(value); + if (normalized !== value || normalized === "." || normalized === ".." || normalized.startsWith("../") || path25.posix.isAbsolute(normalized)) { + throw new Error(`${label} must stay inside the project root`); + } + return value; +} +function parseChecker(value) { + const checker = record6(value, "Native check receipt checker"); + exactKeys2(checker, ["policy", "version", "hash", "limits"], "Native check receipt checker"); + const limits = record6(checker.limits, "Native check receipt limits"); + exactKeys2( + limits, + ["maxFiles", "maxFileBytes", "maxTotalBytes", "maxIssues"], + "Native check receipt limits" + ); + if (checker.policy !== NATIVE_CHECK_POLICY || checker.version !== NATIVE_CHECK_POLICY_VERSION || checker.hash !== NATIVE_CHECKER_HASH || limits.maxFiles !== NATIVE_CHECK_LIMITS.maxFiles || limits.maxFileBytes !== NATIVE_CHECK_LIMITS.maxFileBytes || limits.maxTotalBytes !== NATIVE_CHECK_LIMITS.maxTotalBytes || limits.maxIssues !== NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt checker policy is unsupported"); + } + return { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + hash: NATIVE_CHECKER_HASH, + limits: { ...NATIVE_CHECK_LIMITS } + }; +} +function parseContract(value) { + const contract = record6(value, "Native check receipt contract"); + exactKeys2(contract, ["expectedHash", "beforeHash", "afterHash"], "Native check receipt contract"); + return { + expectedHash: hash5(contract.expectedHash, "Native check expected contract hash"), + beforeHash: hash5(contract.beforeHash, "Native check before contract hash"), + afterHash: hash5(contract.afterHash, "Native check after contract hash") + }; +} +function parseImplementation(value) { + const implementation = record6(value, "Native check receipt implementation"); + exactKeys2( + implementation, + ["scopeHash", "expectedSnapshotHash", "beforeSnapshotHash", "afterSnapshotHash"], + "Native check receipt implementation" + ); + return { + scopeHash: hash5(implementation.scopeHash, "Native check scope hash"), + expectedSnapshotHash: hash5( + implementation.expectedSnapshotHash, + "Native check expected snapshot hash" + ), + beforeSnapshotHash: hash5( + implementation.beforeSnapshotHash, + "Native check before snapshot hash" + ), + afterSnapshotHash: hash5(implementation.afterSnapshotHash, "Native check after snapshot hash") + }; +} +function parseCounts(value) { + const counts = record6(value, "Native check receipt counts"); + exactKeys2( + counts, + [ + "filesSelected", + "filesScanned", + "binaryFilesSkipped", + "bytesScanned", + "issueCount", + "recordedIssueCount" + ], + "Native check receipt counts" + ); + const parsed = { + filesSelected: nonNegativeInteger2(counts.filesSelected, "Native check filesSelected"), + filesScanned: nonNegativeInteger2(counts.filesScanned, "Native check filesScanned"), + binaryFilesSkipped: nonNegativeInteger2( + counts.binaryFilesSkipped, + "Native check binaryFilesSkipped" + ), + bytesScanned: nonNegativeInteger2(counts.bytesScanned, "Native check bytesScanned"), + issueCount: nonNegativeInteger2(counts.issueCount, "Native check issueCount"), + recordedIssueCount: nonNegativeInteger2( + counts.recordedIssueCount, + "Native check recordedIssueCount" + ) + }; + if (parsed.filesScanned + parsed.binaryFilesSkipped > parsed.filesSelected || parsed.filesScanned + parsed.binaryFilesSkipped > NATIVE_CHECK_LIMITS.maxFiles || parsed.bytesScanned > NATIVE_CHECK_LIMITS.maxTotalBytes || parsed.recordedIssueCount > parsed.issueCount || parsed.recordedIssueCount > NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt count accounting is invalid"); + } + return parsed; +} +function compareIssues(left, right) { + return left.path.localeCompare(right.path, "en") || left.line - right.line || ISSUE_KIND_ORDER.indexOf(left.kind) - ISSUE_KIND_ORDER.indexOf(right.kind); +} +function parseIssues(value) { + if (!Array.isArray(value) || value.length > NATIVE_CHECK_LIMITS.maxIssues) { + throw new Error("Native check receipt issues must be a bounded array"); + } + const issues = value.map((entry2, index) => { + const issue = record6(entry2, `Native check issue ${index}`); + exactKeys2(issue, ["path", "line", "kind"], `Native check issue ${index}`); + if (typeof issue.kind !== "string" || !ISSUE_KINDS.has(issue.kind)) { + throw new Error(`Native check issue ${index} kind is invalid`); + } + return { + path: projectRelativePath2(issue.path, `Native check issue ${index} path`), + line: positiveInteger5(issue.line, `Native check issue ${index} line`), + kind: issue.kind + }; + }); + const canonical = [...issues].sort(compareIssues); + if (JSON.stringify(canonical) !== JSON.stringify(issues)) { + throw new Error("Native check receipt issues must be canonical"); + } + return issues; +} +function parseStaleReasons(value) { + if (!Array.isArray(value) || value.some( + (entry2) => typeof entry2 !== "string" || !STALE_REASONS.has(entry2) + )) { + throw new Error("Native check receipt stale reasons are invalid"); + } + const reasons = value; + const canonical = STALE_REASON_ORDER.filter((reason) => reasons.includes(reason)); + if (new Set(reasons).size !== reasons.length || JSON.stringify(canonical) !== JSON.stringify(reasons)) { + throw new Error("Native check receipt stale reasons must be canonical"); + } + return [...reasons]; +} +function nativeCheckInputHash(value) { + return canonicalHash(CHECK_INPUT_HASH_TAG, value); +} +function parseNativeCheckReceipt(value) { + const receipt = record6(value, "Native check receipt"); + exactKeys2( + receipt, + [ + "schema", + "change", + "sourceRevision", + "checker", + "inputHash", + "status", + "startedAt", + "endedAt", + "contract", + "implementation", + "counts", + "issues", + "issuesTruncated", + "stale", + "staleReasons", + "receiptHash" + ], + "Native check receipt" + ); + if (receipt.schema !== NATIVE_CHECK_RECEIPT_SCHEMA) { + throw new Error("Native check receipt schema is invalid"); + } + if (typeof receipt.change !== "string" || Buffer.byteLength(receipt.change, "utf8") > 128 || !CHANGE_NAME_PATTERN3.test(receipt.change)) { + throw new Error("Native check receipt change name is invalid"); + } + const sourceRevision = positiveInteger5(receipt.sourceRevision, "Native check source revision"); + const checker = parseChecker(receipt.checker); + const contract = parseContract(receipt.contract); + const implementation = parseImplementation(receipt.implementation); + const expectedInputHash = nativeCheckInputHash({ + change: receipt.change, + sourceRevision, + checkerHash: checker.hash, + contractHash: contract.expectedHash, + scopeHash: implementation.scopeHash, + snapshotHash: implementation.expectedSnapshotHash + }); + if (hash5(receipt.inputHash, "Native check input hash") !== expectedInputHash) { + throw new Error("Native check receipt input hash mismatch"); + } + if (receipt.status !== "passed" && receipt.status !== "failed") { + throw new Error("Native check receipt status is invalid"); + } + const startedAt = isoTimestamp2(receipt.startedAt, "Native check receipt startedAt"); + const endedAt = isoTimestamp2(receipt.endedAt, "Native check receipt endedAt"); + if (endedAt < startedAt) throw new Error("Native check receipt endedAt precedes startedAt"); + const counts = parseCounts(receipt.counts); + const issues = parseIssues(receipt.issues); + if (counts.recordedIssueCount !== issues.length) { + throw new Error("Native check receipt recorded issue count is inconsistent"); + } + if (typeof receipt.issuesTruncated !== "boolean" || receipt.issuesTruncated !== counts.issueCount > issues.length) { + throw new Error("Native check receipt issue truncation flag is inconsistent"); + } + const staleReasons2 = parseStaleReasons(receipt.staleReasons); + if (typeof receipt.stale !== "boolean" || receipt.stale !== staleReasons2.length > 0) { + throw new Error("Native check receipt stale flag is inconsistent"); + } + const expectedStatus = counts.issueCount === 0 && !receipt.stale ? "passed" : "failed"; + if (receipt.status !== expectedStatus) { + throw new Error("Native check receipt status is inconsistent with its evidence"); + } + if (receipt.status === "passed" && (counts.filesSelected > NATIVE_CHECK_LIMITS.maxFiles || counts.filesScanned + counts.binaryFilesSkipped !== counts.filesSelected)) { + throw new Error("Passed Native check receipt must cover every selected file"); + } + if (counts.filesSelected > NATIVE_CHECK_LIMITS.maxFiles && !issues.some((issue) => issue.kind === "scan-limit")) { + throw new Error("Native check receipt exceeds its file budget without a scan-limit issue"); + } + const content = { + schema: NATIVE_CHECK_RECEIPT_SCHEMA, + change: receipt.change, + sourceRevision, + checker, + inputHash: expectedInputHash, + status: receipt.status, + startedAt, + endedAt, + contract, + implementation, + counts, + issues, + issuesTruncated: receipt.issuesTruncated, + stale: receipt.stale, + staleReasons: staleReasons2 + }; + const receiptHash = hash5(receipt.receiptHash, "Native check receipt content hash"); + if (canonicalHash(NATIVE_CHECK_RECEIPT_HASH_TAG, content) !== receiptHash) { + throw new Error("Native check receipt content hash mismatch"); + } + return { ...content, receiptHash }; +} +function buildNativeCheckReceipt(input) { + const checker = { + policy: NATIVE_CHECK_POLICY, + version: NATIVE_CHECK_POLICY_VERSION, + hash: NATIVE_CHECKER_HASH, + limits: { ...NATIVE_CHECK_LIMITS } + }; + const content = { + schema: NATIVE_CHECK_RECEIPT_SCHEMA, + ...input, + checker, + inputHash: nativeCheckInputHash({ + change: input.change, + sourceRevision: input.sourceRevision, + checkerHash: checker.hash, + contractHash: input.contract.expectedHash, + scopeHash: input.implementation.scopeHash, + snapshotHash: input.implementation.expectedSnapshotHash + }) + }; + return parseNativeCheckReceipt({ + ...content, + receiptHash: canonicalHash(NATIVE_CHECK_RECEIPT_HASH_TAG, content) + }); +} + +// domains/comet-native/native-check-receipt-storage.ts +var HASH_PATTERN11 = /^[a-f0-9]{64}$/u; +var RECEIPT_REF_PATTERN = /^runtime\/evidence\/check-receipts\/([a-f0-9]{64})\.json$/u; +var MAX_NATIVE_CHECK_RECEIPT_BYTES = 512 * 1024; +function sameDirectoryIdentity6(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity7(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function captureDirectoryChain5(root, directory) { + const lexicalRoot = path26.resolve(root); + const lexicalDirectory = path26.resolve(directory); + if (!isInsidePath(lexicalRoot, lexicalDirectory)) { + throw new Error("Native check receipt parent is outside the Native root"); + } + const chain = []; + let cursor = lexicalRoot; + for (const segment of [ + "", + ...path26.relative(lexicalRoot, lexicalDirectory).split(path26.sep).filter(Boolean) + ]) { + if (segment) cursor = path26.join(cursor, segment); + const stat = await fs17.lstat(cursor); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native check receipt parent must be a real directory: ${cursor}`); + } + const realPath = await fs17.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new Error("Native check receipt parent resolves outside the Native root"); + } + chain.push({ + path: cursor, + realPath, + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }); + } + return chain; +} +async function verifyDirectoryChain6(chain) { + for (const identity of chain) { + const stat = await fs17.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity6(identity, stat) || await fs17.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native check receipt parent changed while reading: ${identity.path}`); + } + } +} +function nativeCheckReceiptRef(hash6) { + if (!HASH_PATTERN11.test(hash6)) throw new Error("Native check receipt hash is invalid"); + return `runtime/evidence/check-receipts/${hash6}.json`; +} +function receiptHashFromRef(ref) { + const match = RECEIPT_REF_PATTERN.exec(ref); + if (!match) throw new Error("Native check receipt ref is invalid"); + return match[1]; +} +function receiptFile(paths, name, hash6) { + return path26.join(nativeChangeDir(paths, name), ...nativeCheckReceiptRef(hash6).split("/")); +} +async function readBoundedReceipt(file, changeRoot, nativeRoot) { + const chain = await captureDirectoryChain5(nativeRoot, path26.dirname(file)); + const before = await fs17.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error("Native check receipt must be a regular file"); + } + if (before.size > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + const [realChangeRoot, beforeRealPath] = await Promise.all([ + fs17.realpath(changeRoot), + fs17.realpath(file) + ]); + if (!isInsidePath(realChangeRoot, beforeRealPath)) { + throw new Error("Native check receipt resolves outside its change"); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants4.O_RDONLY | fsConstants4.O_NOFOLLOW | fsConstants4.O_NONBLOCK; + const handle = await fs17.open(file, openFlags).catch((error) => { + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new Error("Native check receipt became unsafe while opening"); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs17.lstat(file), + fs17.realpath(file) + ]); + await verifyDirectoryChain6(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity7(before, opened) || !sameFileIdentity7(opened, pathAfterOpen)) { + throw new Error("Native check receipt changed while opening"); + } + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(16 * 1024); + while (true) { + const remaining = MAX_NATIVE_CHECK_RECEIPT_BYTES + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs17.lstat(file), + fs17.realpath(file) + ]); + await verifyDirectoryChain6(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !isInsidePath(realChangeRoot, afterRealPath) || !sameFileIdentity7(opened, afterHandle) || !sameFileIdentity7(opened, afterPath)) { + throw new Error("Native check receipt changed while reading"); + } + return JSON.parse(Buffer.concat(chunks, total).toString("utf8")); + } finally { + await handle.close(); + } +} +async function readNativeCheckReceipt(paths, name, ref) { + const expectedHash = receiptHashFromRef(ref); + const changeRoot = nativeChangeDir(paths, name); + const file = receiptFile(paths, name, expectedHash); + await resolveContainedNativePath(paths.nativeRoot, file); + const receipt = parseNativeCheckReceipt( + await readBoundedReceipt(file, changeRoot, paths.nativeRoot) + ); + if (receipt.change !== name || receipt.receiptHash !== expectedHash) { + throw new Error("Native check receipt ref/hash/change mismatch"); + } + return receipt; +} +async function writeNativeCheckReceipt(options) { + const receipt = parseNativeCheckReceipt(options.receipt); + if (receipt.change !== options.name) { + throw new Error("Native check receipt change mismatch"); + } + const ref = nativeCheckReceiptRef(receipt.receiptHash); + const file = receiptFile(options.paths, options.name, receipt.receiptHash); + const serializedBytes = Buffer.byteLength(JSON.stringify(receipt, null, 2) + "\n", "utf8"); + if (serializedBytes > MAX_NATIVE_CHECK_RECEIPT_BYTES) { + throw new Error(`Native check receipt exceeds ${MAX_NATIVE_CHECK_RECEIPT_BYTES} bytes`); + } + await resolveContainedNativePath(options.paths.nativeRoot, file); + try { + const existing = await readNativeCheckReceipt(options.paths, options.name, ref); + if (JSON.stringify(existing) !== JSON.stringify(receipt)) { + throw new Error(`Native check receipt hash collision for ${receipt.receiptHash}`); + } + return ref; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, receipt, { containedRoot: options.paths.nativeRoot }); + const persisted = await readNativeCheckReceipt(options.paths, options.name, ref); + if (JSON.stringify(persisted) !== JSON.stringify(receipt)) { + throw new Error("Native check receipt changed while being persisted"); + } + return ref; +} + +// domains/comet-native/native-contract.ts +import path27 from "node:path"; +var CONTRACT_HASH_TAG = "comet.native.contract.v1"; +var ACCEPTANCE_SET_HASH_TAG = "comet.native.acceptance-set.v1"; +var HASH_PATTERN12 = /^[a-f0-9]{64}$/u; +var NATIVE_CONTRACT_LIMITS = { + maxAcceptanceCriteria: NATIVE_ACCEPTANCE_LIMITS.maxCriteria +}; +function portableRef2(value, label) { + const normalized = path27.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path27.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized !== value || normalized === "." || value.endsWith("/")) { + throw new Error(`${label} must be a normalized project-relative ref`); + } + return value; +} +function contentHash(value) { + return canonicalHash("comet.native.contract-content.v1", value.replace(/\r\n?/gu, "\n")); +} +function normalizeSpec3(input) { + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(input.capability)) { + throw new Error(`Invalid Native contract capability: ${input.capability}`); + } + if (input.operation !== "create" && input.operation !== "replace" && input.operation !== "remove") { + throw new Error(`Invalid Native contract operation for ${input.capability}`); + } + if (input.operation === "remove") { + if (input.source !== null || input.markdown !== null || !input.baseHash?.match(HASH_PATTERN12)) { + throw new Error(`Remove contract ${input.capability} requires only a base hash`); + } + return { + snapshot: { + capability: input.capability, + operation: "remove", + source: null, + baseHash: input.baseHash, + contentHash: null + }, + markdown: null + }; + } + if (input.source === null || input.markdown === null) { + throw new Error(`${input.operation} contract ${input.capability} requires source content`); + } + if (input.operation === "create" && input.baseHash !== null) { + throw new Error(`Create contract ${input.capability} requires a null base hash`); + } + if (input.operation === "replace" && !input.baseHash?.match(HASH_PATTERN12)) { + throw new Error(`Replace contract ${input.capability} requires a base hash`); + } + const source = portableRef2(input.source, `Contract source for ${input.capability}`); + return { + snapshot: { + capability: input.capability, + operation: input.operation, + source, + baseHash: input.baseHash, + contentHash: contentHash(input.markdown) + }, + markdown: input.markdown + }; +} +function compareCriteria(left, right) { + return compareText7(left.id, right.id); +} +function compareText7(left, right) { + if (left < right) return -1; + if (left > right) return 1; + return 0; +} +function buildNativeContractSnapshot(input) { + const briefSource = portableRef2(input.briefSource ?? "brief.md", "Native brief source"); + const specs = input.specs.map(normalizeSpec3).sort((left, right) => compareText7(left.snapshot.capability, right.snapshot.capability)); + const capabilities = specs.map(({ snapshot: snapshot2 }) => snapshot2.capability); + if (new Set(capabilities).size !== capabilities.length) { + throw new Error("Native contract contains duplicate capabilities"); + } + const sources = [ + briefSource, + ...specs.flatMap(({ snapshot: snapshot2 }) => snapshot2.source === null ? [] : [snapshot2.source]) + ]; + if (new Set(sources).size !== sources.length) { + throw new Error("Native contract contains duplicate artifact sources"); + } + const acceptance = deriveBriefAcceptanceCriteria( + input.briefMarkdown, + briefSource, + NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria + ); + for (const { snapshot: snapshot2, markdown } of specs) { + if (markdown === null || snapshot2.source === null) continue; + acceptance.push( + ...deriveSpecAcceptanceCriteria( + markdown, + snapshot2.source, + NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria - acceptance.length + ) + ); + } + acceptance.sort(compareCriteria); + if (acceptance.length === 0) { + throw new Error("Native contract has no structured acceptance criteria"); + } + if (acceptance.length > NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria) { + throw new Error( + `Native contract exceeds its ${NATIVE_CONTRACT_LIMITS.maxAcceptanceCriteria}-criterion acceptance budget` + ); + } + const acceptanceIds = acceptance.map(({ id }) => id); + if (new Set(acceptanceIds).size !== acceptanceIds.length) { + throw new Error("Native contract contains duplicate acceptance criteria"); + } + const brief = { source: briefSource, contentHash: contentHash(input.briefMarkdown) }; + const specSnapshots = specs.map(({ snapshot: snapshot2 }) => snapshot2); + const acceptanceHash = canonicalHash(ACCEPTANCE_SET_HASH_TAG, acceptance); + const contractContent = { + schema: "comet.native.contract.v1", + brief, + specs: specSnapshots, + acceptance, + acceptanceHash + }; + return { + ...contractContent, + contractHash: canonicalHash(CONTRACT_HASH_TAG, contractContent) + }; +} + +// domains/comet-native/native-contract-files.ts +var NATIVE_CONTRACT_FILE_LIMITS = { + maxSpecs: 64, + maxFileBytes: DEFAULT_NATIVE_ARTIFACT_MAX_BYTES, + maxTotalBytes: 4 * 1024 * 1024 +}; +async function collectNativeContractFiles(options) { + if (options.specChanges.length > NATIVE_CONTRACT_FILE_LIMITS.maxSpecs) { + throw new Error("Native contract exceeds its spec-count budget"); + } + const brief = await readNativeBoundedTextFile({ + root: options.changeDir, + ref: options.briefRef, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + let totalBytes = brief.size; + const specs = []; + for (const change of options.specChanges) { + if (change.operation === "remove") { + specs.push({ + capability: change.capability, + operation: "remove", + source: null, + baseHash: change.base_hash, + markdown: null + }); + continue; + } + if (!change.source) { + throw new Error(`Native contract ${change.capability} has no proposed spec source`); + } + const source = await readNativeBoundedTextFile({ + root: options.changeDir, + ref: change.source, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + totalBytes += source.size; + if (totalBytes > NATIVE_CONTRACT_FILE_LIMITS.maxTotalBytes) { + throw new Error("Native contract exceeds its total byte budget"); + } + specs.push({ + capability: change.capability, + operation: change.operation, + source: source.ref, + baseHash: change.base_hash, + markdown: source.text + }); + } + return { + contract: buildNativeContractSnapshot({ + briefSource: brief.ref, + briefMarkdown: brief.text, + specs + }), + sourceCount: specs.filter((spec) => spec.source !== null).length + 1, + totalBytes + }; +} + +// domains/comet-native/native-verification-runtime.ts +function projectionManifest(projection) { + return { + schema: "comet.native.content-snapshot.v1", + origin: projection.origin, + createdAt: "1970-01-01T00:00:00.000Z", + complete: projection.complete, + limits: projection.limits, + ...projection.policy ? { policy: projection.policy } : {}, + entries: projection.entries, + omitted: projection.omitted, + omittedCount: projection.omittedCount, + ...projection.omissionOverflow ? { omissionOverflow: projection.omissionOverflow } : {} + }; +} +function nativeRootRef2(paths) { + const value = path28.relative(paths.projectRoot, paths.nativeRoot).replaceAll("\\", "/"); + if (!value || value === ".." || value.startsWith("../") || path28.posix.isAbsolute(value)) { + throw new Error("Native root is outside the project root"); + } + return value; +} +async function currentProjectionHash(options) { + const baseline = projectionManifest(options.bundle.baseline); + const current = await createNativeCurrentContentSnapshot(options.paths, baseline, { + origin: "explicit", + now: options.now + }); + return buildNativeImplementationScopeBundle({ + baseline, + current, + contractHash: options.bundle.scope.contractHash, + declaredArtifacts: options.bundle.scope.declaredArtifacts, + noCodeReason: options.bundle.scope.noCodeReason + }).scope.currentProjectionHash; +} +async function inspectCurrentScopeFacts(options) { + if (!options.state.implementation_scope) { + throw new Error("Native change has no implementation scope"); + } + const bundle = await readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ); + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }); + const currentHash = await currentProjectionHash({ + paths: options.paths, + bundle, + now: options.now + }); + const findingCodes = []; + if (contract.contract.contractHash !== bundle.scope.contractHash) { + findingCodes.push("verification-contract-stale"); + } + if (currentHash !== bundle.scope.currentProjectionHash) { + findingCodes.push("verification-implementation-stale"); + } + return { + bundle, + contract: contract.contract, + contractHash: contract.contract.contractHash, + acceptanceHash: contract.contract.acceptanceHash, + findingCodes + }; +} +async function inspectNativeImplementationScopeFreshness(options) { + if (!options.state.implementation_scope) { + return { freshness: "missing", findingCodes: ["verification-evidence-missing"] }; + } + try { + const facts = await inspectCurrentScopeFacts(options); + const findingCodes = [...new Set(facts.findingCodes)].sort(); + return { + freshness: findingCodes.length === 0 ? "fresh" : "stale", + findingCodes + }; + } catch { + return { freshness: "invalid", findingCodes: ["verification-evidence-invalid"] }; + } +} +async function reportEvidence(options) { + const report = await readNativeBoundedTextFile({ + root: nativeChangeDir(options.paths, options.state.name), + ref: options.reportRef + }); + return { + ref: report.ref, + hash: report.hash, + text: report.text, + entries: parseNativeVerificationMachineBlock(report.text) + }; +} +function checkReceiptBindingCodes(options) { + const { receipt, implementationScope } = options; + const codes = []; + const selectedFiles = implementationScope.scope.changes.filter((change) => change.after !== null); + const selectedBytes = selectedFiles.reduce((total, change) => total + change.after.size, 0); + if (receipt.stale || receipt.sourceRevision !== options.sourceRevision || receipt.contract.expectedHash !== options.contractHash || receipt.contract.beforeHash !== options.contractHash || receipt.contract.afterHash !== options.contractHash || receipt.implementation.scopeHash !== implementationScope.scope.scopeHash || receipt.implementation.expectedSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.implementation.beforeSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.implementation.afterSnapshotHash !== implementationScope.scope.currentProjectionHash || receipt.counts.filesSelected !== selectedFiles.length || receipt.status === "passed" && (receipt.counts.filesScanned + receipt.counts.binaryFilesSkipped !== selectedFiles.length || receipt.counts.bytesScanned !== selectedBytes)) { + codes.push("verification-receipt-stale"); + } + if (options.result === "pass" && receipt.status !== "passed") { + codes.push("verification-receipt-outcome-mismatch"); + } + return codes; +} +async function inspectNativeVerificationEvidence(options) { + if (options.state.phase !== "verify") { + throw new Error(`Native verification evidence requires Verify, got ${options.state.phase}`); + } + const facts = await inspectCurrentScopeFacts(options); + if (facts.findingCodes.length > 0) { + return { + ready: false, + findingCodes: facts.findingCodes, + envelope: null, + evidenceRef: null, + reportSnapshot: null + }; + } + const report = await reportEvidence(options); + let receiptRef = null; + if (options.receiptRef) { + const receipt = await readNativeCheckReceipt( + options.paths, + options.state.name, + options.receiptRef + ); + const receiptCodes = checkReceiptBindingCodes({ + receipt, + sourceRevision: options.state.revision, + result: options.result, + contractHash: facts.contractHash, + implementationScope: facts.bundle + }); + if (receiptCodes.length > 0) { + throw new Error(`Native verification receipt is not admissible: ${receiptCodes.join(", ")}`); + } + receiptRef = options.receiptRef; + } + const trace = buildNativeAcceptanceEvidenceTrace(facts.contract.acceptance, report.entries, { + nativeRootRef: nativeRootRef2(options.paths) + }); + const allowance = options.state.partial_allowance ? await readNativePartialAllowance( + options.paths, + options.state.name, + options.state.partial_allowance + ) : null; + const envelope = buildNativeVerificationEvidenceEnvelope({ + change: options.state.name, + sourceRevision: options.state.revision, + result: options.result, + contractHash: facts.contractHash, + acceptanceHash: facts.acceptanceHash, + implementationScope: { + ref: options.state.implementation_scope, + bundle: facts.bundle + }, + reportRef: report.ref, + reportHash: report.hash, + receiptRef, + acceptanceTrace: trace, + partialAllowance: options.state.partial_allowance && allowance ? { ref: options.state.partial_allowance, allowance } : null, + now: options.now + }); + const evidenceRef = nativeEvidenceRef("verifications", envelope.envelopeHash); + return { + ready: true, + findingCodes: [], + envelope, + evidenceRef, + reportSnapshot: { hash: report.hash, text: report.text } + }; +} +async function persistNativeVerificationEvidence(options) { + if (!options.preparation.ready || options.preparation.envelope === null || options.preparation.evidenceRef === null || options.preparation.reportSnapshot === null) { + throw new Error("Native verification evidence is not ready to persist"); + } + await writeNativeVerificationReportSnapshot({ + paths: options.paths, + name: options.state.name, + ...options.preparation.reportSnapshot + }); + const evidenceRef = await writeNativeVerificationEvidence({ + paths: options.paths, + name: options.state.name, + evidence: options.preparation.envelope + }); + if (evidenceRef !== options.preparation.evidenceRef) { + throw new Error("Native verification evidence persistence ref changed"); + } +} +function emptyEvidence(result2, freshness) { + return { + result: result2, + freshness, + contractHash: null, + acceptanceHash: null, + implementationScopeHash: null, + reportHash: null, + envelopeHash: null, + partialAllowanceHash: null, + skippedAcceptanceCount: 0 + }; +} +async function inspectNativeVerificationFreshness(options) { + if (!options.state.implementation_scope || !options.state.verification_evidence || !options.state.verification_report) { + return { + freshness: "missing", + findingCodes: ["verification-evidence-missing"], + evidence: emptyEvidence(options.state.verification_result, "missing"), + envelope: null + }; + } + try { + const [facts, envelope, report] = await Promise.all([ + inspectCurrentScopeFacts(options), + readNativeVerificationEvidence( + options.paths, + options.state.name, + options.state.verification_evidence + ), + reportEvidence({ + paths: options.paths, + state: options.state, + reportRef: options.state.verification_report + }) + ]); + const findingCodes = [...facts.findingCodes]; + if (report.hash !== envelope.reportHash || report.ref !== envelope.reportRef) { + findingCodes.push("verification-report-stale"); + } + if (envelope.result !== options.state.verification_result || envelope.implementationScopeRef !== options.state.implementation_scope || envelope.partialAllowanceRef !== options.state.partial_allowance || envelope.sourceRevision >= options.state.revision || envelope.contractHash !== facts.bundle.scope.contractHash || envelope.acceptanceCriteriaHash !== facts.acceptanceHash) { + findingCodes.push("verification-state-mismatch"); + } + if (envelope.receiptRef) { + try { + const receipt = await readNativeCheckReceipt( + options.paths, + options.state.name, + envelope.receiptRef + ); + findingCodes.push( + ...checkReceiptBindingCodes({ + receipt, + sourceRevision: envelope.sourceRevision, + result: envelope.result, + contractHash: envelope.contractHash, + implementationScope: facts.bundle + }) + ); + } catch { + findingCodes.push("verification-receipt-invalid"); + } + } + const uniqueCodes = [...new Set(findingCodes)].sort(); + const freshness = uniqueCodes.length > 0 ? "stale" : envelope.freshness; + return { + freshness, + findingCodes: uniqueCodes, + evidence: { + result: options.state.verification_result, + freshness, + contractHash: envelope.contractHash, + acceptanceHash: envelope.acceptanceCriteriaHash, + implementationScopeHash: envelope.implementationScopeHash, + reportHash: envelope.reportHash, + envelopeHash: envelope.envelopeHash, + partialAllowanceHash: envelope.partialAllowanceHash, + skippedAcceptanceCount: envelope.acceptanceTrace.skipped + }, + envelope + }; + } catch { + return { + freshness: "invalid", + findingCodes: ["verification-evidence-invalid"], + evidence: emptyEvidence(options.state.verification_result, "invalid"), + envelope: null + }; + } +} + +// domains/comet-native/native-archive-inspection.ts +function archiveTargetRef(name, now) { + return `archive/${now.toISOString().slice(0, 10)}-${name}`; +} +async function exists2(target) { + try { + await fs18.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function optionalBoundedHash(root, ref) { + try { + return (await readNativeBoundedTextFile({ root, ref })).hash; + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function specFact(paths, name, change) { + const canonical = canonicalSpecPath(paths, change.capability); + if (!isInsidePath(paths.nativeRoot, canonical)) { + throw new Error(`Native canonical spec escapes its root: ${change.capability}`); + } + const canonicalRef = path29.relative(paths.nativeRoot, canonical).replaceAll("\\", "/"); + const actualBaseHash = await optionalBoundedHash(paths.nativeRoot, canonicalRef); + let proposedHash = null; + if (change.operation !== "remove") { + if (!change.source) throw new Error(`Native proposed spec is missing: ${change.capability}`); + proposedHash = (await readNativeBoundedTextFile({ + root: nativeChangeDir(paths, name), + ref: change.source + })).hash; + } + return { + capability: change.capability, + operation: change.operation, + expectedBaseHash: change.operation === "create" ? null : change.base_hash, + actualBaseHash, + proposedHash + }; +} +async function hasPendingTransition(paths, name) { + return exists2(nativeTransitionJournalFile(paths, name)); +} +async function inspectNativeArchivePreflight(options) { + const now = options.now ?? /* @__PURE__ */ new Date(); + const state = await readNativeChange(options.paths, options.name); + const targetRef = archiveTargetRef(state.name, now); + const target = path29.resolve(options.paths.nativeRoot, ...targetRef.split("/")); + if (!isInsidePath(options.paths.nativeRoot, target)) { + throw new Error("Native archive target escapes its root"); + } + const [ + specs, + evidence, + conflicts, + pendingSchema, + pendingCheckpoint, + pendingTransition, + targetExists + ] = await Promise.all([ + Promise.all(state.spec_changes.map((change) => specFact(options.paths, state.name, change))), + inspectNativeVerificationFreshness({ paths: options.paths, state, now }), + inspectNativeChangeConflicts(options.paths, state.name), + hasPendingNativeSchemaMigration(options.paths, state.name), + hasPendingNativeCheckpointRecovery(options.paths, state.name), + hasPendingTransition(options.paths, state.name), + exists2(target) + ]); + return buildNativeArchivePreflight({ + change: state.name, + stateSchema: state.schema, + revision: state.revision, + phase: state.phase, + archived: state.archived, + pendingJournal: pendingSchema || pendingCheckpoint || pendingTransition, + targetRef, + targetExists, + specs, + evidence: evidence.evidence, + findingCodes: [...evidence.findingCodes, ...conflicts.findingCodes] + }); +} + +// domains/comet-native/native-archive-content.ts +import { promises as fs19 } from "node:fs"; +import path30 from "node:path"; +var TREE_HASH_TAG = "comet.native.archive-tree.v1"; +var NATIVE_ARCHIVE_CONTENT_LIMITS = { + maxDepth: 128, + maxEntries: 2e4, + maxFileBytes: 64 * 1024 * 1024, + maxTotalBytes: 256 * 1024 * 1024, + maxManifestBytes: 16 * 1024 * 1024, + maxRefBytes: 4 * 1024 +}; +function normalizedLimits(limits) { + const resolved = { ...NATIVE_ARCHIVE_CONTENT_LIMITS, ...limits }; + for (const [name, value] of Object.entries(resolved)) { + if (!Number.isSafeInteger(value) || value <= 0) { + throw new Error(`Native Archive content limit ${name} must be a positive safe integer`); + } + } + return resolved; +} +function directorySnapshot(entries) { + return JSON.stringify( + entries.map((entry2) => ({ + name: entry2.name, + kind: entry2.isDirectory() ? "directory" : entry2.isFile() ? "file" : entry2.isSymbolicLink() ? "symlink" : "other" + })).sort((left, right) => left.name.localeCompare(right.name)) + ); +} +function appendTreeEntry(entries, entry2, budget, limits) { + budget.entryCount += 1; + if (budget.entryCount > limits.maxEntries) { + throw new Error(`Native Archive content exceeds ${limits.maxEntries} entries`); + } + if (Buffer.byteLength(entry2.ref, "utf8") > limits.maxRefBytes) { + throw new Error(`Native Archive content ref exceeds ${limits.maxRefBytes} bytes: ${entry2.ref}`); + } + const entryBytes = Buffer.byteLength(JSON.stringify(entry2), "utf8"); + budget.manifestBytes += entryBytes + (entries.length === 0 ? 0 : 1); + if (budget.manifestBytes + 2 > limits.maxManifestBytes) { + throw new Error(`Native Archive content manifest exceeds ${limits.maxManifestBytes} bytes`); + } + entries.push(entry2); +} +async function walkArchiveTree(root, directory, entries, budget, limits, depth) { + if (depth > limits.maxDepth) { + throw new Error(`Native Archive content exceeds depth ${limits.maxDepth}`); + } + const protectedDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: `Native Archive content directory ${path30.relative(root, directory) || "."}`, + maxEntries: limits.maxEntries + }); + const beforeSnapshot = directorySnapshot(protectedDirectory.entries); + const children = [...protectedDirectory.entries]; + children.sort((left, right) => left.name.localeCompare(right.name)); + for (const child of children) { + await protectedDirectory.verify(); + const target = path30.join(directory, child.name); + const ref = path30.relative(root, target).replaceAll("\\", "/"); + const stat = await fs19.lstat(target); + if (child.isSymbolicLink() || stat.isSymbolicLink()) { + throw new Error(`Native Archive content must not contain symlinks or junctions: ${ref}`); + } + if (child.isDirectory() && stat.isDirectory()) { + appendTreeEntry(entries, { ref, kind: "directory" }, budget, limits); + await walkArchiveTree(root, target, entries, budget, limits, depth + 1); + continue; + } + if (!child.isFile() || !stat.isFile()) { + throw new Error(`Native Archive content must contain only files and directories: ${ref}`); + } + const snapshot2 = await readNativeProtectedFile({ + root, + file: target, + maxBytes: limits.maxFileBytes, + label: `Native Archive content file ${ref}` + }); + budget.totalBytes += snapshot2.size; + if (budget.totalBytes > limits.maxTotalBytes) { + throw new Error(`Native Archive content exceeds ${limits.maxTotalBytes} total file bytes`); + } + appendTreeEntry( + entries, + { ref, kind: "file", hash: snapshot2.hash, size: snapshot2.size }, + budget, + limits + ); + await protectedDirectory.verify(); + } + await protectedDirectory.verify(); + const afterDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: `Native Archive content directory ${path30.relative(root, directory) || "."}`, + maxEntries: limits.maxEntries + }); + if (directorySnapshot(afterDirectory.entries) !== beforeSnapshot) { + throw new Error( + `Native Archive content directory changed while reading: ${path30.relative(root, directory) || "."}` + ); + } + await Promise.all([protectedDirectory.verify(), afterDirectory.verify()]); +} +async function hashNativeArchiveTree(directory, requestedLimits = {}) { + const limits = normalizedLimits(requestedLimits); + directory = path30.resolve(directory); + const stat = await fs19.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native Archive move source must be a real directory: ${directory}`); + } + const entries = []; + const budget = { entryCount: 0, totalBytes: 0, manifestBytes: 0 }; + await walkArchiveTree(directory, directory, entries, budget, limits, 0); + const manifest = JSON.stringify(entries); + if (Buffer.byteLength(manifest, "utf8") > limits.maxManifestBytes) { + throw new Error(`Native Archive content manifest exceeds ${limits.maxManifestBytes} bytes`); + } + return sha256Text(`${TREE_HASH_TAG}\0${manifest}`); +} +async function inspectNativeArchiveContent(target, requestedLimits = {}) { + const limits = normalizedLimits(requestedLimits); + target = path30.resolve(target); + let stat; + try { + stat = await fs19.lstat(target); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + if (stat.isSymbolicLink()) { + throw new Error(`Native Archive transaction path must not be a symlink or junction: ${target}`); + } + if (stat.isFile()) { + const snapshot2 = await readNativeProtectedFile({ + root: path30.dirname(target), + file: target, + maxBytes: limits.maxFileBytes, + label: `Native Archive transaction file ${path30.basename(target)}` + }); + return { kind: "file", hash: snapshot2.hash }; + } + if (stat.isDirectory()) { + return { kind: "directory", hash: await hashNativeArchiveTree(target, limits) }; + } + throw new Error(`Native Archive transaction path has an unsupported file type: ${target}`); +} + +// domains/comet-native/native-archive-transaction.ts +import { promises as fs20 } from "node:fs"; +import path31 from "node:path"; +var NATIVE_ARCHIVE_COPY_MAX_BYTES = 16 * 1024 * 1024; +var NATIVE_ARCHIVE_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_ARCHIVE_CAS_RECORD_MAX_BYTES = 16 * 1024; +var NATIVE_ARCHIVE_CAS_SCHEMA = "comet.native.archive-cas.v1"; +function resolveRefLexically2(paths, ref) { + const target = path31.resolve(paths.nativeRoot, ...ref.split("/")); + if (path31.relative(paths.nativeRoot, target).split(path31.sep).includes("..")) { + throw new Error(`Unsafe Native Archive transaction ref: ${ref}`); + } + return target; +} +async function resolveRef2(paths, ref) { + return resolveContainedNativePath(paths.nativeRoot, resolveRefLexically2(paths, ref)); +} +function sameContent(actual, expectedHash, expectedKind = "file") { + return expectedHash === null ? actual === null : actual?.kind === expectedKind && actual.hash === expectedHash; +} +function contentDescription(value) { + return value === null ? "missing" : `${value.kind}:${value.hash}`; +} +async function assertContent(options) { + const actual = await inspectNativeArchiveContent(options.target); + if (!sameContent(actual, options.expectedHash, options.expectedKind)) { + throw new Error( + `${options.label} content changed: expected ${options.expectedHash ?? "missing"}, got ${contentDescription(actual)}` + ); + } + return actual; +} +async function createNativeArchiveTransactionV2(paths, journal) { + const validated = parseNativeArchiveTransactionJournalV2(journal); + const tx = await resolveNativeTransactionPaths(paths, validated.id); + await fs20.mkdir(tx.staged, { recursive: true }); + await fs20.mkdir(tx.backups, { recursive: true }); + await atomicWriteJson(tx.journal, validated, { containedRoot: paths.nativeRoot }); + await appendNativeTransactionEvent(paths, validated.id, "prepared"); +} +async function readNativeArchiveTransactionV2(paths, id) { + const tx = await resolveNativeTransactionPaths(paths, id); + const snapshot2 = await readNativeProtectedFile({ + root: paths.nativeRoot, + file: tx.journal, + maxBytes: NATIVE_ARCHIVE_JOURNAL_MAX_BYTES, + label: `Native Archive transaction journal ${id}` + }); + const journal = parseNativeArchiveTransactionJournalV2( + JSON.parse(snapshot2.bytes.toString("utf8")) + ); + if (journal.id !== id) throw new Error(`Invalid Native Archive transaction journal: ${id}`); + return journal; +} +async function setStatus(paths, journal, status) { + const updated = parseNativeArchiveTransactionJournalV2({ ...journal, status }); + const tx = await resolveNativeTransactionPaths(paths, updated.id); + await atomicWriteJson(tx.journal, updated, { containedRoot: paths.nativeRoot }); + return updated; +} +function fileObjectIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +function fileVersion(stat) { + return { + ...fileObjectIdentity(stat), + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileObject2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs; +} +function sameFileVersion(expected, actual) { + return sameFileObject2(expected, actual) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function pathExists(target) { + try { + await fs20.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function captureStableArchiveFile(options) { + const before = await fs20.lstat(options.file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + const beforeVersion = fileVersion(before); + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: options.file, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: options.label + }); + const after = await fs20.lstat(options.file); + if (!after.isFile() || after.isSymbolicLink() || !sameFileVersion(beforeVersion, fileVersion(after)) || snapshot2.hash !== options.expectedHash) { + throw new Error(`${options.label} content changed or object identity changed while binding`); + } + return { identity: fileObjectIdentity(after), size: snapshot2.size }; +} +function parseCasRecord(value, operation, role, expectedHash) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Archive CAS ${operation.id} ${role} record must be an object`); + } + const record8 = value; + const keys = Object.keys(record8).sort(); + if (keys.join(",") !== "hash,identity,operationId,role,schema") { + throw new Error(`Archive CAS ${operation.id} ${role} record has an invalid shape`); + } + const identity = record8.identity; + if (!identity || typeof identity !== "object" || Array.isArray(identity)) { + throw new Error(`Archive CAS ${operation.id} ${role} identity must be an object`); + } + const identityRecord = identity; + if (Object.keys(identityRecord).sort().join(",") !== "birthtimeMs,dev,ino") { + throw new Error(`Archive CAS ${operation.id} ${role} identity has an invalid shape`); + } + for (const field2 of ["dev", "ino", "birthtimeMs"]) { + if (typeof identityRecord[field2] !== "number" || !Number.isFinite(identityRecord[field2]) || identityRecord[field2] < 0) { + throw new Error(`Archive CAS ${operation.id} ${role} identity ${field2} is invalid`); + } + } + if (record8.schema !== NATIVE_ARCHIVE_CAS_SCHEMA || record8.operationId !== operation.id || record8.role !== role || record8.hash !== expectedHash) { + throw new Error(`Archive CAS ${operation.id} ${role} record is not transaction-bound`); + } + return { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: operation.id, + role, + hash: expectedHash, + identity: { + dev: identityRecord.dev, + ino: identityRecord.ino, + birthtimeMs: identityRecord.birthtimeMs + } + }; +} +async function archiveCasPaths(paths, journal, operation, target) { + const tx = await resolveNativeTransactionPaths(paths, journal.id); + const directory = path31.join(tx.backups, ".cas"); + const prefix = `.${path31.basename(target)}.comet-archive-cas-${journal.id}-${operation.id}`; + const result2 = { + directory, + originalRecord: path31.join(directory, `${operation.id}.original.json`), + postRecord: path31.join(directory, `${operation.id}.post.json`), + candidate: path31.join(directory, `${operation.id}.candidate`), + originalQuarantine: path31.join(path31.dirname(target), `${prefix}.original`), + rollbackQuarantine: path31.join(path31.dirname(target), `${prefix}.rollback`) + }; + await Promise.all( + Object.values(result2).map((item) => resolveContainedNativePath(paths.nativeRoot, item)) + ); + return result2; +} +async function readCasRecord(options) { + if (!await pathExists(options.file)) return null; + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: options.file, + maxBytes: NATIVE_ARCHIVE_CAS_RECORD_MAX_BYTES, + label: `Archive CAS ${options.operation.id} ${options.role} record` + }); + return parseCasRecord( + JSON.parse(snapshot2.bytes.toString("utf8")), + options.operation, + options.role, + options.expectedHash + ); +} +async function persistCasRecord(options) { + const existing = await readCasRecord(options); + if (existing) { + if (!sameFileObject2(existing.identity, options.identity)) { + throw new Error( + `Archive CAS ${options.operation.id} ${options.role} object identity changed` + ); + } + return existing; + } + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.file), + label: `Archive CAS ${options.operation.id} records` + }); + const record8 = { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: options.role, + hash: options.expectedHash, + identity: options.identity + }; + try { + await atomicWriteJson(options.file, record8, { + containedRoot: options.paths.nativeRoot, + exclusive: true + }); + } catch (error) { + if (error.code !== "EEXIST") throw error; + } + const persisted = await readCasRecord(options); + if (!persisted || !sameFileObject2(persisted.identity, options.identity)) { + throw new Error(`Archive CAS ${options.operation.id} ${options.role} record changed`); + } + return persisted; +} +async function validateFileAgainstCasRecord(options) { + const current = await captureStableArchiveFile({ + paths: options.paths, + file: options.file, + expectedHash: options.record.hash, + label: options.label + }); + if (!sameFileObject2(options.record.identity, current.identity)) { + throw new Error(`${options.label} object identity changed`); + } +} +async function bindCurrentTarget(options) { + const current = await captureStableArchiveFile({ + paths: options.paths, + file: options.file, + expectedHash: options.expectedHash, + label: options.label + }); + const record8 = await persistCasRecord({ + paths: options.paths, + file: options.recordFile, + operation: options.operation, + role: options.role, + expectedHash: options.expectedHash, + identity: current.identity + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.file, + operation: options.operation, + record: record8, + label: options.label + }); + return record8; +} +async function restoreUnexpectedQuarantine(options) { + if (!await pathExists(options.quarantine) || await pathExists(options.target)) return; + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive CAS ${options.operation.id} quarantine restoration` + }); + await guard.verify(); + try { + await fs20.link(options.quarantine, options.target); + } catch (error) { + if (error.code === "EEXIST") return; + throw error; + } + await guard.verify(); + const [quarantine, target] = await Promise.all([ + fs20.lstat(options.quarantine), + fs20.lstat(options.target) + ]); + if (!sameFileObject2(fileObjectIdentity(quarantine), fileObjectIdentity(target))) { + throw new Error(`Archive CAS ${options.operation.id} could not restore quarantined content`); + } + await fs20.unlink(options.quarantine); + await guard.verify(); +} +async function quarantineBoundTarget(options) { + if (await pathExists(options.quarantine)) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} quarantine ${options.operation.target}` + }); + return; + } + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive ${options.phase} target ${options.operation.target}` + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} target ${options.operation.target}` + }); + await options.hooks?.afterArchiveTargetBound?.(options.phase, options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} target ${options.operation.target}` + }); + try { + await fs20.rename(options.target, options.quarantine); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive ${options.phase} quarantine ${options.operation.target}` + }); + } catch (error) { + await restoreUnexpectedQuarantine(options); + throw new Error( + `Archive ${options.phase} target ${options.operation.target} changed during quarantine`, + { cause: error } + ); + } + await options.hooks?.afterArchiveTargetQuarantined?.( + options.phase, + options.operation, + options.quarantine + ); +} +async function removeExactCasFile(options) { + if (!await pathExists(options.file)) return; + await validateFileAgainstCasRecord(options); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.file), + label: options.label + }); + await guard.verify(); + await validateFileAgainstCasRecord(options); + await fs20.unlink(options.file); + await guard.verify(); +} +async function ensureBackup(paths, operation, hooks) { + if (!operation.backup || operation.expectedTargetHash === null) return; + const target = await resolveRef2(paths, operation.target); + const backup = await resolveRef2(paths, operation.backup); + const existing = await inspectNativeArchiveContent(backup); + if (existing !== null) { + if (!sameContent(existing, operation.expectedTargetHash)) { + throw new Error(`Archive transaction backup content changed: ${operation.backup}`); + } + return; + } + await assertContent({ + target, + expectedHash: operation.expectedTargetHash, + label: `Archive transaction target ${operation.target}` + }); + await copyNativeProtectedFile({ + sourceRoot: paths.nativeRoot, + source: target, + targetRoot: paths.nativeRoot, + target: backup, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: `Archive transaction backup ${operation.backup}`, + expectedHash: operation.expectedTargetHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => hooks?.afterProtectedCopySourceParentCaptured?.("backup", operation.target) + } + }); + await assertContent({ + target: backup, + expectedHash: operation.expectedTargetHash, + label: `Archive transaction backup ${operation.backup}` + }); +} +async function ensureOriginalTargetQuarantined(options) { + const expectedHash = options.operation.expectedTargetHash; + if (expectedHash === null) { + throw new Error( + `Archive operation ${options.operation.id} has no original target to quarantine` + ); + } + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + let record8 = await readCasRecord({ + paths: options.paths, + file: cas.originalRecord, + operation: options.operation, + role: "original", + expectedHash + }); + if (await pathExists(cas.originalQuarantine)) { + if (!record8) { + throw new Error( + `Archive apply quarantine ${options.operation.target} has no bound object identity` + ); + } + await validateFileAgainstCasRecord({ + paths: options.paths, + file: cas.originalQuarantine, + operation: options.operation, + record: record8, + label: `Archive apply quarantine ${options.operation.target}` + }); + await ensureBackup(options.paths, options.operation, options.hooks); + return { record: record8, quarantine: cas.originalQuarantine }; + } + if (!await pathExists(options.target)) { + throw new Error( + `Archive apply target ${options.operation.target} disappeared before quarantine` + ); + } + record8 ??= await bindCurrentTarget({ + paths: options.paths, + file: options.target, + recordFile: cas.originalRecord, + operation: options.operation, + role: "original", + expectedHash, + label: `Archive apply target ${options.operation.target}` + }); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: record8, + label: `Archive apply target ${options.operation.target}` + }); + await ensureBackup(options.paths, options.operation, options.hooks); + await quarantineBoundTarget({ + paths: options.paths, + operation: options.operation, + phase: "apply", + target: options.target, + quarantine: cas.originalQuarantine, + record: record8, + hooks: options.hooks + }); + return { record: record8, quarantine: cas.originalQuarantine }; +} +async function ensureWriteCandidate(options) { + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + if (await pathExists(cas.candidate)) { + await captureStableArchiveFile({ + paths: options.paths, + file: cas.candidate, + expectedHash: options.operation.stagedHash, + label: `Archive write candidate ${options.operation.target}` + }); + return cas.candidate; + } + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: cas.directory, + label: `Archive CAS ${options.operation.id} records` + }); + await copyNativeProtectedFile({ + sourceRoot: options.paths.nativeRoot, + source: options.staged, + targetRoot: options.paths.nativeRoot, + target: cas.candidate, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES, + label: `Archive transaction staged file ${options.operation.staged}`, + expectedHash: options.operation.stagedHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => options.hooks?.afterProtectedCopySourceParentCaptured?.("apply", options.operation.staged) + } + }); + return cas.candidate; +} +async function ensureWriteInstalled(options) { + const expectedHash = options.operation.stagedHash; + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + let record8 = await readCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash + }); + if (record8 && await pathExists(options.target)) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: record8, + label: `Archive write target ${options.operation.target}` + }); + if (await pathExists(cas.candidate)) { + await removeExactCasFile({ + paths: options.paths, + file: cas.candidate, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + } + return record8; + } + if (await pathExists(options.target)) { + const candidate2 = await ensureWriteCandidate(options); + const candidateIdentity2 = await captureStableArchiveFile({ + paths: options.paths, + file: candidate2, + expectedHash, + label: `Archive write candidate ${options.operation.target}` + }); + const targetIdentity2 = await captureStableArchiveFile({ + paths: options.paths, + file: options.target, + expectedHash, + label: `Archive write target ${options.operation.target}` + }); + if (!sameFileObject2(candidateIdentity2.identity, targetIdentity2.identity)) { + throw new Error( + `Archive write target ${options.operation.target} is occupied by an external object` + ); + } + record8 = await persistCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash, + identity: targetIdentity2.identity + }); + await removeExactCasFile({ + paths: options.paths, + file: candidate2, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + return record8; + } + const candidate = await ensureWriteCandidate(options); + const candidateIdentity = await captureStableArchiveFile({ + paths: options.paths, + file: candidate, + expectedHash, + label: `Archive write candidate ${options.operation.target}` + }); + await ensureNativeProtectedDirectory({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive write target ${options.operation.target}` + }); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive write target ${options.operation.target}` + }); + await options.hooks?.beforeArchiveTargetInstall?.("apply", options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: candidate, + operation: options.operation, + record: { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: "post", + hash: expectedHash, + identity: candidateIdentity.identity + }, + label: `Archive write candidate ${options.operation.target}` + }); + try { + await fs20.link(candidate, options.target); + } catch (error) { + if (error.code === "EEXIST") { + throw new Error( + `Archive write target ${options.operation.target} was created before exclusive install`, + { cause: error } + ); + } + throw error; + } + await guard.verify(); + await options.hooks?.afterArchiveTargetInstalled?.("apply", options.operation, options.target); + const targetIdentity = await captureStableArchiveFile({ + paths: options.paths, + file: options.target, + expectedHash, + label: `Archive write target ${options.operation.target}` + }); + if (!sameFileObject2(candidateIdentity.identity, targetIdentity.identity)) { + throw new Error(`Archive write target ${options.operation.target} changed during install`); + } + record8 = await persistCasRecord({ + paths: options.paths, + file: cas.postRecord, + operation: options.operation, + role: "post", + expectedHash, + identity: targetIdentity.identity + }); + await removeExactCasFile({ + paths: options.paths, + file: candidate, + operation: options.operation, + record: record8, + label: `Archive write candidate ${options.operation.target}` + }); + return record8; +} +async function applyWrite(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + const staged = await resolveRef2(paths, operation.staged); + await assertContent({ + target: staged, + expectedHash: operation.stagedHash, + label: `Archive transaction staged file ${operation.staged}` + }); + if (operation.expectedTargetHash !== null) { + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord || !await pathExists(target)) { + await ensureOriginalTargetQuarantined({ paths, journal, operation, target, hooks }); + } + } else { + const actual = await inspectNativeArchiveContent(target); + if (actual !== null && !sameContent(actual, operation.stagedHash)) { + throw new Error( + `Archive transaction target ${operation.target} changed before create: ${contentDescription(actual)}` + ); + } + } + await ensureWriteInstalled({ + paths, + journal, + operation, + staged, + target, + hooks + }); + await assertContent({ + target, + expectedHash: operation.stagedHash, + label: `Archive transaction target ${operation.target}` + }); +} +async function applyRemove(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + await ensureOriginalTargetQuarantined({ paths, journal, operation, target, hooks }); + await assertContent({ + target, + expectedHash: null, + label: `Archive transaction target ${operation.target}` + }); +} +async function applyMove(paths, operation) { + const source = await resolveRef2(paths, operation.source); + const target = await resolveRef2(paths, operation.target); + const [sourceContent, targetContent] = await Promise.all([ + inspectNativeArchiveContent(source), + inspectNativeArchiveContent(target) + ]); + if (sourceContent === null && sameContent(targetContent, operation.expectedSourceHash, "directory")) { + return; + } + if (!sameContent(sourceContent, operation.expectedSourceHash, "directory") || targetContent !== null) { + throw new Error( + `Archive transaction move ${operation.id} content changed: source=${contentDescription(sourceContent)}, target=${contentDescription(targetContent)}` + ); + } + await fs20.mkdir(path31.dirname(target), { recursive: true }); + await fs20.rename(source, target); + await assertContent({ + target, + expectedHash: operation.expectedSourceHash, + expectedKind: "directory", + label: `Archive transaction move target ${operation.target}` + }); +} +async function applyOperation2(paths, journal, operation, hooks) { + if (operation.type === "write") return applyWrite(paths, journal, operation, hooks); + if (operation.type === "remove") return applyRemove(paths, journal, operation, hooks); + return applyMove(paths, operation); +} +function completedOperationIds(options) { + let operationIndex = 0; + let startedCurrent = false; + const completed = []; + for (const event of options.events) { + if (event.type !== "operation-started" && event.type !== "operation-completed") continue; + const expected = options.journal.operations[operationIndex]; + if (!expected || event.operationId !== expected.id) { + throw new Error( + `Native Archive transaction ${options.journal.id} operation events are out of order` + ); + } + if (event.type === "operation-started") { + startedCurrent = true; + continue; + } + if (!startedCurrent) { + throw new Error( + `Native Archive transaction ${options.journal.id} completed an operation before it started` + ); + } + completed.push(expected.id); + operationIndex += 1; + startedCurrent = false; + } + return completed; +} +async function assertCompletedOperation(paths, journal, operation, finalizationStarted) { + const target = await resolveRef2(paths, operation.target); + if (operation.type === "write") { + const staged = await resolveRef2(paths, operation.staged); + await assertContent({ + target: staged, + expectedHash: operation.stagedHash, + label: `Completed Archive staged file ${operation.staged}` + }); + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord) { + throw new Error(`Completed Archive target ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: target, + operation, + record: postRecord, + label: `Completed Archive target ${operation.target}` + }); + if (operation.expectedTargetHash !== null) { + await assertContent({ + target: await resolveRef2(paths, operation.backup), + expectedHash: operation.expectedTargetHash, + label: `Completed Archive backup ${operation.backup}` + }); + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error( + `Completed Archive target ${operation.target} has no original CAS identity` + ); + } + if (await pathExists(cas.originalQuarantine)) { + await validateFileAgainstCasRecord({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Completed Archive original quarantine ${operation.target}` + }); + } else if (!finalizationStarted) { + throw new Error(`Completed Archive original quarantine ${operation.target} disappeared`); + } + } + return; + } + if (operation.type === "remove") { + await assertContent({ + target, + expectedHash: null, + label: `Completed Archive target ${operation.target}` + }); + await assertContent({ + target: await resolveRef2(paths, operation.backup), + expectedHash: operation.expectedTargetHash, + label: `Completed Archive backup ${operation.backup}` + }); + const cas = await archiveCasPaths(paths, journal, operation, target); + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error(`Completed Archive remove ${operation.target} has no original CAS identity`); + } + if (await pathExists(cas.originalQuarantine)) { + await validateFileAgainstCasRecord({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Completed Archive original quarantine ${operation.target}` + }); + } else if (!finalizationStarted) { + throw new Error(`Completed Archive original quarantine ${operation.target} disappeared`); + } + return; + } + if (finalizationStarted) return; + await assertContent({ + target: await resolveRef2(paths, operation.source), + expectedHash: null, + label: `Completed Archive move source ${operation.source}` + }); + await assertContent({ + target, + expectedHash: operation.expectedSourceHash, + expectedKind: "directory", + label: `Completed Archive move target ${operation.target}` + }); +} +async function applyNativeArchiveTransactionV2(paths, journal, hooks) { + let current = journal.status === "prepared" ? await setStatus(paths, journal, "applying") : journal; + if (current.status !== "applying") { + throw new Error(`Native Archive transaction ${current.id} cannot apply from ${current.status}`); + } + const events = await readNativeTransactionEvents(paths, current.id); + const completedIds = completedOperationIds({ journal: current, events }); + const completed = new Set(completedIds); + const finalizationStarted = events.some((event) => event.type === "archive-finalization-started"); + for (const operation of current.operations.slice(0, completedIds.length)) { + await assertCompletedOperation(paths, current, operation, finalizationStarted); + } + let completedCount = completed.size; + for (const operation of current.operations) { + if (completed.has(operation.id)) continue; + await appendNativeTransactionEvent(paths, current.id, "operation-started", operation.id); + await applyOperation2(paths, current, operation, hooks); + await appendNativeTransactionEvent(paths, current.id, "operation-completed", operation.id); + completedCount += 1; + await hooks?.afterOperation?.(operation, completedCount); + } + current = await readNativeArchiveTransactionV2(paths, current.id); + return current; +} +async function fileMatchesCasRecord(options) { + try { + await validateFileAgainstCasRecord(options); + return true; + } catch { + return false; + } +} +async function installOriginalTarget(options) { + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive rollback original quarantine ${options.operation.target}` + }); + const guard = await captureNativeProtectedDirectoryGuard({ + root: options.paths.nativeRoot, + directory: path31.dirname(options.target), + label: `Archive rollback target ${options.operation.target}` + }); + await options.hooks?.beforeArchiveTargetInstall?.("rollback", options.operation, options.target); + await guard.verify(); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.quarantine, + operation: options.operation, + record: options.record, + label: `Archive rollback original quarantine ${options.operation.target}` + }); + try { + await fs20.link(options.quarantine, options.target); + } catch (error) { + if (error.code === "EEXIST") { + throw new Error( + `Archive rollback target ${options.operation.target} was created before exclusive restore`, + { cause: error } + ); + } + throw error; + } + await guard.verify(); + await options.hooks?.afterArchiveTargetInstalled?.("rollback", options.operation, options.target); + await validateFileAgainstCasRecord({ + paths: options.paths, + file: options.target, + operation: options.operation, + record: options.record, + label: `Archive rollback target ${options.operation.target}` + }); +} +async function removeWriteCandidateIfPresent(options) { + if (options.operation.type !== "write") return; + const cas = await archiveCasPaths( + options.paths, + options.journal, + options.operation, + options.target + ); + if (!await pathExists(cas.candidate)) return; + const current = await captureStableArchiveFile({ + paths: options.paths, + file: cas.candidate, + expectedHash: options.operation.stagedHash, + label: `Archive write candidate ${options.operation.target}` + }); + await removeExactCasFile({ + paths: options.paths, + file: cas.candidate, + operation: options.operation, + record: { + schema: NATIVE_ARCHIVE_CAS_SCHEMA, + operationId: options.operation.id, + role: "post", + hash: options.operation.stagedHash, + identity: current.identity + }, + label: `Archive write candidate ${options.operation.target}` + }); +} +async function rollbackWriteOrRemove(paths, journal, operation, hooks) { + const target = await resolveRef2(paths, operation.target); + const cas = await archiveCasPaths(paths, journal, operation, target); + const originalHash = operation.expectedTargetHash; + const originalRecord = originalHash === null ? null : await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: originalHash + }); + let postRecord = operation.type === "write" ? await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }) : null; + if (operation.type === "write" && !postRecord && await pathExists(cas.candidate) && await pathExists(target)) { + const candidate = await captureStableArchiveFile({ + paths, + file: cas.candidate, + expectedHash: operation.stagedHash, + label: `Archive write candidate ${operation.target}` + }); + const current = await captureStableArchiveFile({ + paths, + file: target, + expectedHash: operation.stagedHash, + label: `Archive rollback target ${operation.target}` + }); + if (sameFileObject2(candidate.identity, current.identity)) { + postRecord = await persistCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash, + identity: current.identity + }); + } + } + if (await pathExists(cas.rollbackQuarantine)) { + if (!postRecord) { + throw new Error(`Archive rollback quarantine ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: cas.rollbackQuarantine, + operation, + record: postRecord, + label: `Archive rollback quarantine ${operation.target}` + }); + } + if (await pathExists(target)) { + const alreadyRestored = originalRecord !== null && await fileMatchesCasRecord({ + paths, + file: target, + operation, + record: originalRecord, + label: `Archive rollback target ${operation.target}` + }); + if (!alreadyRestored) { + if (!postRecord) { + if (!originalRecord) { + const actual = await inspectNativeArchiveContent(target); + if (sameContent(actual, originalHash)) { + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + return; + } + } + throw new Error( + `Archive rollback target ${operation.target} is occupied by an external object` + ); + } + await quarantineBoundTarget({ + paths, + operation, + phase: "rollback", + target, + quarantine: cas.rollbackQuarantine, + record: postRecord, + hooks + }); + } + } + if (originalHash === null) { + if (await pathExists(target)) { + throw new Error(`Archive rollback create target ${operation.target} could not be removed`); + } + } else if (!await pathExists(target)) { + if (!originalRecord || !await pathExists(cas.originalQuarantine)) { + throw new Error( + `Archive rollback original target ${operation.target} cannot be recovered safely` + ); + } + await installOriginalTarget({ + paths, + operation, + target, + quarantine: cas.originalQuarantine, + record: originalRecord, + hooks + }); + } + if (originalRecord && await pathExists(cas.originalQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Archive rollback original quarantine ${operation.target}` + }); + } + if (postRecord && await pathExists(cas.rollbackQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.rollbackQuarantine, + operation, + record: postRecord, + label: `Archive rollback quarantine ${operation.target}` + }); + } + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + await assertContent({ + target, + expectedHash: originalHash, + label: `Archive rollback target ${operation.target}` + }); +} +async function rollbackMove(paths, operation) { + const source = await resolveRef2(paths, operation.source); + const target = await resolveRef2(paths, operation.target); + const [sourceContent, targetContent] = await Promise.all([ + inspectNativeArchiveContent(source), + inspectNativeArchiveContent(target) + ]); + if (sameContent(sourceContent, operation.expectedSourceHash, "directory") && targetContent === null) { + return; + } + if (sourceContent !== null || !sameContent(targetContent, operation.expectedSourceHash, "directory")) { + throw new Error( + `Archive rollback move ${operation.id} content changed: source=${contentDescription(sourceContent)}, target=${contentDescription(targetContent)}` + ); + } + await fs20.mkdir(path31.dirname(source), { recursive: true }); + await fs20.rename(target, source); +} +async function rollbackNativeArchiveTransactionV2(paths, journal, hooks) { + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some( + (event) => event.type === "archive-finalization-started" || event.type === "archive-finalized" + )) { + throw new Error("An archive whose finalization started can only be recovered by continuing it"); + } + let current = await setStatus(paths, journal, "rolling-back"); + await appendNativeTransactionEvent(paths, current.id, "rollback-started"); + const started = new Set( + events.filter((event) => event.type === "operation-started" || event.type === "operation-completed").map((event) => event.operationId) + ); + for (const operation of [...current.operations].reverse()) { + if (!started.has(operation.id)) continue; + if (operation.type === "move") await rollbackMove(paths, operation); + else await rollbackWriteOrRemove(paths, current, operation, hooks); + } + await appendNativeTransactionEvent(paths, current.id, "rollback-completed"); + current = await setStatus(paths, current, "rolled-back"); + return current; +} +async function assertCommittedArchiveCasTarget(paths, journal, operation) { + const target = await resolveRef2(paths, operation.target); + if (operation.type === "write") { + const cas = await archiveCasPaths(paths, journal, operation, target); + const postRecord = await readCasRecord({ + paths, + file: cas.postRecord, + operation, + role: "post", + expectedHash: operation.stagedHash + }); + if (!postRecord) { + throw new Error(`Archive commit target ${operation.target} has no CAS identity`); + } + await validateFileAgainstCasRecord({ + paths, + file: target, + operation, + record: postRecord, + label: `Archive commit target ${operation.target}` + }); + return; + } + await assertContent({ + target, + expectedHash: null, + label: `Archive commit target ${operation.target}` + }); +} +async function cleanupCommittedArchiveCas(paths, journal) { + for (const operation of journal.operations) { + if (operation.type === "move") continue; + const target = await resolveRef2(paths, operation.target); + const cas = await archiveCasPaths(paths, journal, operation, target); + if (await pathExists(cas.rollbackQuarantine)) { + throw new Error(`Archive commit found an unfinished rollback for ${operation.target}`); + } + await assertCommittedArchiveCasTarget(paths, journal, operation); + if (operation.expectedTargetHash !== null) { + const originalRecord = await readCasRecord({ + paths, + file: cas.originalRecord, + operation, + role: "original", + expectedHash: operation.expectedTargetHash + }); + if (!originalRecord) { + throw new Error(`Archive commit target ${operation.target} has no original CAS identity`); + } + if (await pathExists(cas.originalQuarantine)) { + await removeExactCasFile({ + paths, + file: cas.originalQuarantine, + operation, + record: originalRecord, + label: `Archive commit original quarantine ${operation.target}` + }); + } + } + await removeWriteCandidateIfPresent({ paths, journal, operation, target }); + } + for (const operation of journal.operations) { + if (operation.type !== "move") { + await assertCommittedArchiveCasTarget(paths, journal, operation); + } + } +} +async function finalizeNativeArchiveTransactionV2(paths, journal, event) { + if (event === "commit") await cleanupCommittedArchiveCas(paths, journal); + await appendNativeTransactionEvent(paths, journal.id, event); + return event === "commit" ? setStatus(paths, journal, "committed") : journal; +} + +// domains/comet-native/native-checkpoint-journal.ts +import { randomUUID as randomUUID4 } from "crypto"; +import { promises as fs21 } from "fs"; + +// domains/comet-native/native-checkpoint-storage.ts +import path32 from "path"; +var NATIVE_CHECKPOINT_LIMITS = { + maxArtifacts: 128, + maxFileBytes: 16 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxDocumentBytes: 256 * 1024 +}; +var HASH_PATTERN13 = /^[a-f0-9]{64}$/u; +function record7(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be an object`); + } + return value; +} +function exactKeys3(value, keys, label) { + const expected = new Set(keys); + const unknown = Object.keys(value).filter((key) => !expected.has(key)); + const missing = keys.filter((key) => !(key in value)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); + if (missing.length > 0) throw new Error(`${label} is missing field(s): ${missing.join(", ")}`); +} +function stringValue(value, label, max = 2e3) { + if (typeof value !== "string" || value.length === 0 || value.length > max) { + throw new Error(`${label} must be a non-empty string of at most ${max} characters`); + } + return value; +} +function positiveInteger6(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function nonNegativeInteger3(value, label) { + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`${label} must be a non-negative integer`); + } + return value; +} +function normalizeNativeCheckpointArtifactRef(value) { + const trimmed = value.trim().replaceAll("\\", "/"); + if (trimmed.length === 0 || path32.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|\/)/u.test(trimmed) || trimmed.split("/").includes("..")) { + throw new Error(`Checkpoint artifact must be project-relative: ${value}`); + } + const normalized = path32.posix.normalize(trimmed); + if (normalized === "." || normalized === ".." || normalized.startsWith("../")) { + throw new Error(`Checkpoint artifact must name a project file: ${value}`); + } + return normalized; +} +function nativeProgressCheckpointFile(paths, name) { + return path32.join(nativeChangeDir(paths, name), "runtime", "checkpoints", "progress.json"); +} +function nativeCheckpointJournalFile(paths, name) { + return path32.join(nativeChangeDir(paths, name), "runtime", "checkpoint-journal.json"); +} +function nativeCheckpointManifestFile(paths, name, hash6) { + if (!HASH_PATTERN13.test(hash6)) throw new Error("Native checkpoint manifest hash is invalid"); + return path32.join( + nativeChangeDir(paths, name), + "runtime", + "checkpoints", + "manifests", + `${hash6}.json` + ); +} +function nativeCheckpointManifestRef(hash6) { + if (!HASH_PATTERN13.test(hash6)) throw new Error("Native checkpoint manifest hash is invalid"); + return `runtime/checkpoints/manifests/${hash6}.json`; +} +async function readBoundedJson(root, file, label) { + const snapshot2 = await readNativeProtectedFile({ + root, + file, + maxBytes: NATIVE_CHECKPOINT_LIMITS.maxDocumentBytes, + label + }); + return JSON.parse(snapshot2.bytes.toString("utf8")); +} +function parseArtifact(value, index) { + const artifact = record7(value, `checkpoint manifest artifact ${index}`); + exactKeys3(artifact, ["path", "hash", "size"], `checkpoint manifest artifact ${index}`); + const artifactPath = normalizeNativeCheckpointArtifactRef( + stringValue(artifact.path, `checkpoint artifact ${index} path`, 4096) + ); + const sensitiveReason = nativeSensitiveRelativePathReason(artifactPath); + if (sensitiveReason) { + throw new Error( + `checkpoint artifact ${index} is excluded as sensitive (${sensitiveReason}): ${artifactPath}` + ); + } + if (typeof artifact.hash !== "string" || !HASH_PATTERN13.test(artifact.hash)) { + throw new Error(`checkpoint artifact ${index} hash is invalid`); + } + return { + path: artifactPath, + hash: artifact.hash, + size: nonNegativeInteger3(artifact.size, `checkpoint artifact ${index} size`) + }; +} +function parseNativeCheckpointManifestValue(value, expectedName) { + const manifest = record7(value, "Native checkpoint manifest"); + exactKeys3( + manifest, + ["schema", "change", "artifacts", "totalBytes"], + "Native checkpoint manifest" + ); + if (manifest.schema !== "comet.native.checkpoint-manifest.v1") { + throw new Error("Native checkpoint manifest schema is invalid"); + } + if (manifest.change !== expectedName) + throw new Error("Native checkpoint manifest change mismatch"); + if (!Array.isArray(manifest.artifacts)) { + throw new Error("Native checkpoint manifest artifacts must be an array"); + } + if (manifest.artifacts.length > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error("Native checkpoint manifest has too many artifacts"); + } + const artifacts = manifest.artifacts.map(parseArtifact); + const sorted = [...artifacts].sort((left, right) => left.path.localeCompare(right.path)); + if (JSON.stringify(artifacts) !== JSON.stringify(sorted)) { + throw new Error("Native checkpoint manifest artifacts must be sorted"); + } + if (new Set(artifacts.map((artifact) => artifact.path)).size !== artifacts.length) { + throw new Error("Native checkpoint manifest has duplicate artifacts"); + } + const totalBytes = nonNegativeInteger3(manifest.totalBytes, "checkpoint manifest totalBytes"); + if (artifacts.reduce((total, artifact) => total + artifact.size, 0) !== totalBytes) { + throw new Error("Native checkpoint manifest totalBytes mismatch"); + } + if (totalBytes > NATIVE_CHECKPOINT_LIMITS.maxTotalBytes) { + throw new Error("Native checkpoint manifest totalBytes exceeds its budget"); + } + return { + schema: "comet.native.checkpoint-manifest.v1", + change: expectedName, + artifacts, + totalBytes + }; +} +function hashNativeCheckpointManifest(manifest) { + return sha256Text(JSON.stringify(parseNativeCheckpointManifestValue(manifest, manifest.change))); +} +function parseNativeProgressCheckpointValue(value, expectedName) { + const checkpoint = record7(value, "Native progress checkpoint"); + exactKeys3( + checkpoint, + [ + "schema", + "id", + "change", + "phase", + "previousRevision", + "stateRevision", + "summary", + "nextAction", + "inputHash", + "manifestHash", + "manifestRef", + "artifactCount", + "createdAt" + ], + "Native progress checkpoint" + ); + if (checkpoint.schema !== "comet.native.progress-checkpoint.v1") { + throw new Error("Native progress checkpoint schema is invalid"); + } + if (checkpoint.change !== expectedName) throw new Error("Native checkpoint change mismatch"); + const phase = checkpoint.phase; + if (phase !== "shape" && phase !== "build" && phase !== "verify" && phase !== "archive") { + throw new Error("Native checkpoint phase is invalid"); + } + const previousRevision = positiveInteger6( + checkpoint.previousRevision, + "Native checkpoint previousRevision" + ); + const stateRevision = positiveInteger6( + checkpoint.stateRevision, + "Native checkpoint stateRevision" + ); + if (stateRevision !== previousRevision + 1) { + throw new Error("Native checkpoint stateRevision must increment previousRevision once"); + } + const manifestHash = stringValue(checkpoint.manifestHash, "Native checkpoint manifestHash", 64); + if (!HASH_PATTERN13.test(manifestHash)) + throw new Error("Native checkpoint manifestHash is invalid"); + const expectedManifestRef = nativeCheckpointManifestRef(manifestHash); + if (checkpoint.manifestRef !== expectedManifestRef) { + throw new Error("Native checkpoint manifestRef does not match manifestHash"); + } + const inputHash = stringValue(checkpoint.inputHash, "Native checkpoint inputHash", 64); + if (!HASH_PATTERN13.test(inputHash)) throw new Error("Native checkpoint inputHash is invalid"); + const createdAt = stringValue(checkpoint.createdAt, "Native checkpoint createdAt", 64); + if (Number.isNaN(Date.parse(createdAt))) + throw new Error("Native checkpoint createdAt is invalid"); + const artifactCount = nonNegativeInteger3( + checkpoint.artifactCount, + "Native checkpoint artifactCount" + ); + if (artifactCount > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error("Native checkpoint artifactCount exceeds its budget"); + } + const summary = stringValue(checkpoint.summary, "Native checkpoint summary"); + const nextAction = stringValue(checkpoint.nextAction, "Native checkpoint nextAction"); + if (redactNativeCredentialText(summary) !== summary || redactNativeCredentialText(nextAction) !== nextAction) { + throw new Error("Native checkpoint text contains unredacted credential material"); + } + return { + schema: "comet.native.progress-checkpoint.v1", + id: stringValue(checkpoint.id, "Native checkpoint id", 128), + change: expectedName, + phase, + previousRevision, + stateRevision, + summary, + nextAction, + inputHash, + manifestHash, + manifestRef: expectedManifestRef, + artifactCount, + createdAt + }; +} +function parseNativeCheckpointJournalValue(value, expectedName) { + const journal = record7(value, "Native checkpoint journal"); + exactKeys3( + journal, + [ + "schema", + "id", + "change", + "inputHash", + "createdAt", + "previousState", + "nextState", + "checkpoint", + "manifest" + ], + "Native checkpoint journal" + ); + if (journal.schema !== "comet.native.checkpoint-journal.v1") { + throw new Error("Native checkpoint journal schema is invalid"); + } + if (journal.change !== expectedName) throw new Error("Native checkpoint journal change mismatch"); + const previousState = parseNativeChangeValue(journal.previousState); + const nextState = parseNativeChangeValue(journal.nextState); + const checkpoint = parseNativeProgressCheckpointValue(journal.checkpoint, expectedName); + const manifest = parseNativeCheckpointManifestValue(journal.manifest, expectedName); + const inputHash = stringValue(journal.inputHash, "Native checkpoint journal inputHash", 64); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: checkpoint.summary, + nextAction: checkpoint.nextAction, + artifacts: manifest.artifacts + }) + ); + if (!HASH_PATTERN13.test(inputHash) || inputHash !== checkpoint.inputHash || inputHash !== expectedInputHash || journal.id !== checkpoint.id || journal.createdAt !== checkpoint.createdAt) { + throw new Error("Native checkpoint journal envelope mismatch"); + } + if (previousState.name !== expectedName || nextState.name !== expectedName || nextState.revision !== previousState.revision + 1 || checkpoint.previousRevision !== previousState.revision || checkpoint.stateRevision !== nextState.revision || checkpoint.phase !== nextState.phase || checkpoint.manifestHash !== hashNativeCheckpointManifest(manifest) || checkpoint.artifactCount !== manifest.artifacts.length) { + throw new Error("Native checkpoint journal state mismatch"); + } + return { + schema: "comet.native.checkpoint-journal.v1", + id: checkpoint.id, + change: expectedName, + inputHash, + createdAt: checkpoint.createdAt, + previousState, + nextState, + checkpoint, + manifest + }; +} +async function hashProjectArtifact(paths, artifactRef, hooks) { + const target = path32.resolve(paths.projectRoot, ...artifactRef.split("/")); + if (!isInsidePath(paths.projectRoot, target) || isInsidePath(paths.nativeRoot, target)) { + throw new Error(`Checkpoint artifact is outside project content: ${artifactRef}`); + } + const sensitiveReason = nativeSensitiveArtifactReason(paths, artifactRef); + if (sensitiveReason) { + throw new Error( + `Checkpoint artifact is excluded as sensitive (${sensitiveReason}): ${artifactRef}` + ); + } + const snapshot2 = await readNativeProtectedFile({ + root: paths.projectRoot, + file: target, + maxBytes: NATIVE_CHECKPOINT_LIMITS.maxFileBytes, + label: `Checkpoint artifact ${artifactRef}`, + forbiddenRoots: [paths.nativeRoot], + hooks: { + afterParentChainCaptured: () => hooks?.afterParentChainCaptured?.(artifactRef), + afterOpen: () => hooks?.afterOpen?.(artifactRef), + beforeRead: () => hooks?.beforeRead?.(artifactRef) + } + }); + return { path: artifactRef, hash: snapshot2.hash, size: snapshot2.size }; +} +async function createNativeCheckpointManifest(paths, name, artifactRefs, hooks) { + const normalized = artifactRefs.map(normalizeNativeCheckpointArtifactRef).sort(); + if (normalized.length > NATIVE_CHECKPOINT_LIMITS.maxArtifacts) { + throw new Error( + `Checkpoint supports at most ${NATIVE_CHECKPOINT_LIMITS.maxArtifacts} artifacts` + ); + } + if (new Set(normalized).size !== normalized.length) { + throw new Error("Checkpoint artifacts must not contain duplicates"); + } + const artifacts = []; + let totalBytes = 0; + for (const artifactRef of normalized) { + const artifact = await hashProjectArtifact(paths, artifactRef, hooks); + totalBytes += artifact.size; + if (totalBytes > NATIVE_CHECKPOINT_LIMITS.maxTotalBytes) { + throw new Error("Checkpoint artifacts exceed the total byte budget"); + } + artifacts.push(artifact); + } + return { + schema: "comet.native.checkpoint-manifest.v1", + change: name, + artifacts, + totalBytes + }; +} +async function readNativeProgressCheckpoint(paths, name) { + const file = nativeProgressCheckpointFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + return parseNativeProgressCheckpointValue( + await readBoundedJson(paths.nativeRoot, file, "Native progress checkpoint"), + name + ); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function readNativeCheckpointManifest(paths, name, hash6) { + const file = nativeCheckpointManifestFile(paths, name, hash6); + await resolveContainedNativePath(paths.nativeRoot, file); + const value = await readBoundedJson(paths.nativeRoot, file, "Native checkpoint manifest"); + const manifest = parseNativeCheckpointManifestValue(value, name); + assertCheckpointManifestSafeForPaths(paths, manifest); + if (hashNativeCheckpointManifest(manifest) !== hash6) { + throw new Error("Native checkpoint manifest content hash mismatch"); + } + return manifest; +} +async function readNativeCheckpointJournal(paths, name) { + const file = nativeCheckpointJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const journal = parseNativeCheckpointJournalValue( + await readBoundedJson(paths.nativeRoot, file, "Native checkpoint journal"), + name + ); + assertCheckpointManifestSafeForPaths(paths, journal.manifest); + return journal; + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function writeNativeCheckpointManifest(paths, name, manifest, hooks) { + const parsed = parseNativeCheckpointManifestValue(manifest, name); + assertCheckpointManifestSafeForPaths(paths, parsed); + const hash6 = hashNativeCheckpointManifest(parsed); + const file = nativeCheckpointManifestFile(paths, name, hash6); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const existing = await readNativeCheckpointManifest(paths, name, hash6); + if (JSON.stringify(existing) !== JSON.stringify(parsed)) { + throw new Error("Native checkpoint manifest hash collision"); + } + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + await atomicWriteJson(file, parsed, { + containedRoot: paths.nativeRoot, + beforeCommit: hooks?.beforeCommit + }); + return hash6; +} +function assertCheckpointManifestSafeForPaths(paths, manifest) { + for (const artifact of manifest.artifacts) { + const reason = nativeSensitiveArtifactReason(paths, artifact.path); + if (reason) { + throw new Error( + `Native checkpoint manifest contains a sensitive artifact (${reason}): ${artifact.path}` + ); + } + } +} +async function writeNativeProgressCheckpoint(paths, checkpoint) { + const parsed = parseNativeProgressCheckpointValue(checkpoint, checkpoint.change); + const manifest = await readNativeCheckpointManifest(paths, parsed.change, parsed.manifestHash); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: parsed.summary, + nextAction: parsed.nextAction, + artifacts: manifest.artifacts + }) + ); + if (parsed.inputHash !== expectedInputHash || parsed.artifactCount !== manifest.artifacts.length) { + throw new Error("Native progress checkpoint does not match its artifact manifest"); + } + const file = nativeProgressCheckpointFile(paths, checkpoint.change); + await resolveContainedNativePath(paths.nativeRoot, file); + await atomicWriteJson(file, parsed, { containedRoot: paths.nativeRoot }); +} +async function writeNativeCheckpointJournal(paths, journal) { + const parsed = parseNativeCheckpointJournalValue(journal, journal.change); + const file = nativeCheckpointJournalFile(paths, journal.change); + await resolveContainedNativePath(paths.nativeRoot, file); + if (await readNativeCheckpointJournal(paths, journal.change)) { + throw new Error(`Native checkpoint recovery is already pending for ${journal.change}`); + } + await atomicWriteJson(file, parsed, { containedRoot: paths.nativeRoot }); +} +async function inspectNativeCheckpointFreshness(options) { + let checkpoint; + try { + checkpoint = await readNativeProgressCheckpoint(options.paths, options.name); + } catch (error) { + return { + checkpoint: null, + manifest: null, + freshness: "stale", + reasons: ["checkpoint-progress-invalid"], + findings: [ + { + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint is invalid: ${error.message}. Automatic repair is unavailable; inspect and move the invalid checkpoint file aside before retrying`, + path: nativeProgressCheckpointFile(options.paths, options.name) + } + ] + }; + } + if (!checkpoint) { + return { + checkpoint: null, + manifest: null, + freshness: "fresh", + reasons: ["no-checkpoint"], + findings: [] + }; + } + const reasons = []; + const findings = []; + if (checkpoint.stateRevision !== options.stateRevision) reasons.push("state-revision-changed"); + let manifest = null; + try { + manifest = await readNativeCheckpointManifest( + options.paths, + options.name, + checkpoint.manifestHash + ); + const expectedInputHash = sha256Text( + JSON.stringify({ + summary: checkpoint.summary, + nextAction: checkpoint.nextAction, + artifacts: manifest.artifacts + }) + ); + if (checkpoint.inputHash !== expectedInputHash || checkpoint.artifactCount !== manifest.artifacts.length) { + throw new Error("Native progress checkpoint does not match its artifact manifest"); + } + for (const expected of manifest.artifacts) { + try { + const actual = await hashProjectArtifact(options.paths, expected.path); + if (actual.hash !== expected.hash || actual.size !== expected.size) { + reasons.push(`artifact-changed:${expected.path}`); + } + } catch { + reasons.push(`artifact-unavailable:${expected.path}`); + } + } + } catch (error) { + reasons.push("checkpoint-manifest-invalid"); + findings.push({ + code: "checkpoint-manifest-invalid", + message: `Native checkpoint manifest is invalid: ${error.message}`, + path: nativeCheckpointManifestFile(options.paths, options.name, checkpoint.manifestHash) + }); + } + return { + checkpoint, + manifest, + freshness: reasons.length === 0 ? "fresh" : "stale", + reasons, + findings + }; +} + +// domains/comet-native/native-checkpoint-journal.ts +async function prepareNativeCheckpointJournal(options) { + const createdAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString(); + const id = options.checkpointId?.() ?? randomUUID4(); + const checkpoint = { + ...options.checkpoint, + id, + createdAt + }; + const journal = { + schema: "comet.native.checkpoint-journal.v1", + id, + change: options.previousState.name, + inputHash: checkpoint.inputHash, + createdAt, + previousState: options.previousState, + nextState: options.nextState, + checkpoint, + manifest: options.manifest + }; + await writeNativeCheckpointManifest(options.paths, options.previousState.name, options.manifest); + await writeNativeCheckpointJournal(options.paths, journal); + return journal; +} +async function continueNativeCheckpointLocked(paths, name, hooks) { + const journal = await readNativeCheckpointJournal(paths, name); + if (!journal) return null; + const manifestHash = await writeNativeCheckpointManifest(paths, journal.change, journal.manifest); + if (manifestHash !== journal.checkpoint.manifestHash) { + throw new Error("Native checkpoint recovery manifest hash mismatch"); + } + await compareAndSwapNativeChangeLocked(paths, journal.nextState, journal.previousState.revision, { + allowPendingCheckpointRecovery: true + }); + await hooks?.afterStateWritten?.(journal); + await writeNativeProgressCheckpoint(paths, journal.checkpoint); + await hooks?.afterProgressWritten?.(journal); + await fs21.rm(nativeCheckpointJournalFile(paths, name), { force: true }); + return journal; +} +async function continueNativeCheckpoint(paths, name, hooks) { + return withNativeMutationLock( + paths, + `continue checkpoint ${name}`, + () => withNativeTransitionLock(paths, name, `continue checkpoint ${name}`, async () => { + await continueNativeTransitionLocked(paths, name); + return continueNativeCheckpointLocked(paths, name, hooks); + }) + ); +} + +// domains/comet-native/native-change-recovery.ts +async function settleNativeChangeJournalsLocked(paths, name) { + await continueNativeTransitionLocked(paths, name); + await continueNativeCheckpointLocked(paths, name); +} + +// domains/comet-entry/current-selection.ts +import { randomUUID as randomUUID5 } from "crypto"; +import { promises as fs22 } from "fs"; +import path33 from "path"; +var COMET_CURRENT_SELECTION_SCHEMA = "comet.selection.v2"; +var COMET_CURRENT_SELECTION_MAX_BYTES = 16 * 1024; +function cometCurrentSelectionFile(projectRoot) { + return path33.join(projectRoot, ".comet", "current-change.json"); +} +function isRecord(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function validBranch(value) { + return value === null || typeof value === "string"; +} +function parseSelection(source) { + let value; + try { + value = JSON.parse(source); + } catch (error) { + throw new Error( + `current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + if (!isRecord(value)) { + throw new Error("current change selection must be a JSON object"); + } + if (value.version === 1) { + if (typeof value.change !== "string") { + throw new Error("legacy current change selection change must be a string"); + } + if (value.branch !== void 0 && !validBranch(value.branch)) { + throw new Error("legacy current change selection branch must be a string or null"); + } + return { + selection: { + schema: COMET_CURRENT_SELECTION_SCHEMA, + workflow: "classic", + change: value.change, + branch: value.branch ?? null + }, + legacy: true + }; + } + if (value.schema !== COMET_CURRENT_SELECTION_SCHEMA) { + throw new Error(`current change selection schema must be ${COMET_CURRENT_SELECTION_SCHEMA}`); + } + if (value.workflow !== "native" && value.workflow !== "classic") { + throw new Error("current change selection workflow must be native or classic"); + } + if (typeof value.change !== "string") { + throw new Error("current change selection change must be a string"); + } + if (!validBranch(value.branch)) { + throw new Error("current change selection branch must be a string or null"); + } + if (value.workflow === "native" && value.branch !== null) { + throw new Error("Native current change selection branch must be null"); + } + return { selection: value, legacy: false }; +} +async function readCometCurrentSelection(projectRoot) { + let source; + try { + const file = cometCurrentSelectionFile(projectRoot); + const { bytes } = await readFileRaceSafe(file, COMET_CURRENT_SELECTION_MAX_BYTES, { + label: "current change selection" + }); + source = bytes.toString("utf8"); + } catch (error) { + if (error.code === "ENOENT") return { status: "missing" }; + throw new Error( + `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + const parsed = parseSelection(source); + return { status: "selected", ...parsed }; +} +async function writeCometCurrentSelection(projectRoot, selection) { + const parsed = parseSelection(JSON.stringify(selection)); + if (parsed.legacy) throw new Error("cannot write a legacy current change selection"); + const file = cometCurrentSelectionFile(projectRoot); + const temporary = `${file}.${randomUUID5()}.tmp`; + await fs22.mkdir(path33.dirname(file), { recursive: true }); + try { + await fs22.writeFile(temporary, `${JSON.stringify(parsed.selection, null, 2)} +`, "utf8"); + await fs22.rename(temporary, file); + } catch (error) { + await fs22.rm(temporary, { force: true }); + throw error; + } +} +async function clearCometCurrentSelection(projectRoot) { + await fs22.rm(cometCurrentSelectionFile(projectRoot), { force: true }); +} +async function clearCometCurrentSelectionIf(projectRoot, workflow, change) { + const current = await readCometCurrentSelection(projectRoot); + if (current.status !== "selected" || current.selection.workflow !== workflow || current.selection.change !== change) { + return false; + } + await clearCometCurrentSelection(projectRoot); + return true; +} + +// domains/comet-native/native-selection.ts +var NATIVE_SELECTION_MAX_BYTES = 16 * 1024; +async function readNativeSelectionRecord(paths) { + const current = await readCometCurrentSelection(paths.projectRoot); + if (current.status === "missing" || current.selection.workflow !== "native") return null; + assertNativeName(current.selection.change); + return current.selection; +} +function nativeSelectionFile(paths) { + return cometCurrentSelectionFile(paths.projectRoot); +} +async function selectNativeChange(paths, name) { + return withNativeMutationLock(paths, `select change ${name}`, async () => { + assertNativeName(name); + await readNativeChange(paths, name); + await writeCometCurrentSelection(paths.projectRoot, { + schema: "comet.selection.v2", + workflow: "native", + change: name, + branch: null + }); + }); +} +async function resolveSelectedNativeChange(paths) { + const value = await readNativeSelectionRecord(paths); + if (!value) return null; + await readNativeChange(paths, value.change); + return value.change; +} +async function clearNativeSelectionIfLocked(paths, name) { + await assertNoPendingNativeRootMove(paths.projectRoot); + return clearCometCurrentSelectionIf(paths.projectRoot, "native", name); +} + +// domains/comet-native/native-archive.ts +var NATIVE_ARCHIVE_COPY_MAX_BYTES2 = 16 * 1024 * 1024; +var NativeSpecConflictError = class extends Error { + constructor(capability, expectedHash, actualHash, canonicalPath) { + super( + `Canonical spec conflict for ${capability}: expected ${expectedHash ?? "(missing)"}, actual ${actualHash ?? "(missing)"}` + ); + this.capability = capability; + this.expectedHash = expectedHash; + this.actualHash = actualHash; + this.canonicalPath = canonicalPath; + this.name = "NativeSpecConflictError"; + } + capability; + expectedHash; + actualHash; + canonicalPath; + code = "native-spec-conflict"; +}; +var NativeArchivePreflightError = class extends Error { + constructor(preflight, message = preflight.ready ? "Native Archive preflight no longer matches the expected hash" : `Native Archive preflight is blocked: ${preflight.findingCodes.join(", ")}`) { + super(message); + this.preflight = preflight; + this.name = "NativeArchivePreflightError"; + } + preflight; + code = "native-archive-preflight"; +}; +async function optionalHash2(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +function assertArchiveReady(state) { + if (state.phase !== "archive") throw new Error(`Native change ${state.name} is not in Archive`); + if (state.verification_result !== "pass") { + throw new Error(`Native change ${state.name} has not passed verification`); + } + if (!state.verification_report) { + throw new Error(`Native change ${state.name} has no verification report`); + } + if (state.archived) throw new Error(`Native change ${state.name} is already archived`); +} +async function assertArchiveArtifacts(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const brief = await validateNativeBrief(changeDir, state.brief); + const verification = await validateNativeVerification(changeDir, state.verification_report); + const findings = [...brief.findings, ...verification.findings]; + if (findings.length > 0) { + throw new Error(`Native archive artifacts are invalid: ${findings[0].message}`); + } +} +async function assertSpecBase(paths, change) { + const canonical = canonicalSpecPath(paths, change.capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const actual = await optionalHash2(canonical); + const expected = change.operation === "create" ? null : change.base_hash; + if (actual !== expected) { + throw new NativeSpecConflictError(change.capability, expected, actual, canonical); + } +} +function archiveTarget(paths, name, now) { + return path34.join(paths.archiveDir, `${now.toISOString().slice(0, 10)}-${name}`); +} +async function pathExists2(target) { + try { + await fs23.access(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function buildArchiveJournal(options) { + const { paths, state, now, transactionId, preflight } = options; + const target = archiveTarget(paths, state.name, now); + if (await pathExists2(target)) throw new Error(`Native archive target already exists: ${target}`); + if (nativeRootRef(paths, target) !== preflight.targetRef) { + throw new Error("Native Archive target changed after preflight"); + } + const tx = await resolveNativeTransactionPaths(paths, transactionId); + const operations = []; + for (const [index, change] of state.spec_changes.entries()) { + await assertSpecBase(paths, change); + const canonical = canonicalSpecPath(paths, change.capability); + const preview = preflight.operations.find( + (operation) => operation.capability === change.capability && operation.operation === change.operation + ); + if (!preview) { + throw new Error(`Native Archive preflight has no operation for ${change.capability}`); + } + if (change.operation !== "remove" && preview.proposedHash === null) { + throw new Error(`Native Archive preflight has no proposed hash for ${change.capability}`); + } + const backup = path34.join(tx.backups, "specs", change.capability, "spec.md"); + if (change.operation === "remove") { + operations.push({ + id: `spec-${index + 1}-${change.capability}`, + type: "remove", + target: nativeRootRef(paths, canonical), + backup: nativeRootRef(paths, backup), + expectedTargetHash: change.base_hash + }); + continue; + } + const changeDir = nativeChangeDir(paths, state.name); + await resolveNativeArtifactFile(changeDir, change.source); + const source2 = path34.resolve(changeDir, ...change.source.split(/[\\/]/u)); + const staged = path34.join(tx.staged, "specs", change.capability, "spec.md"); + const stagedSnapshot = await copyNativeProtectedFile({ + sourceRoot: changeDir, + source: source2, + targetRoot: paths.nativeRoot, + target: staged, + maxBytes: NATIVE_ARCHIVE_COPY_MAX_BYTES2, + label: `Native Archive proposed spec ${change.capability}`, + expectedHash: preview.proposedHash, + expectedTargetHash: null, + exclusive: true, + hooks: { + afterParentChainCaptured: () => options.hooks?.afterProtectedCopySourceParentCaptured?.("stage", change.source) + } + }); + const stagedHash = stagedSnapshot.hash; + if (stagedHash !== preview.proposedHash) { + throw new Error(`Proposed Native spec changed after preflight: ${change.capability}`); + } + operations.push({ + id: `spec-${index + 1}-${change.capability}`, + type: "write", + target: nativeRootRef(paths, canonical), + staged: nativeRootRef(paths, staged), + ...change.operation === "replace" ? { backup: nativeRootRef(paths, backup) } : {}, + expectedTargetHash: change.operation === "create" ? null : change.base_hash, + stagedHash + }); + } + const source = nativeChangeDir(paths, state.name); + operations.push({ + id: "archive-change", + type: "move", + source: nativeRootRef(paths, source), + target: nativeRootRef(paths, target), + expectedSourceHash: await hashNativeArchiveTree(source), + expectedTargetHash: null + }); + return { + schema: "comet.native.transaction.v2", + id: transactionId, + kind: "archive", + status: "prepared", + change: state.name, + createdAt: now.toISOString(), + preflightHash: preflight.preflightHash, + operations + }; +} +function archiveDirectoryFromJournal(paths, journal) { + const operation = journal.operations.find((item) => item.id === "archive-change"); + if (!operation || operation.type !== "move") { + throw new Error(`Archive transaction ${journal.id} has no archive move`); + } + return path34.resolve(paths.nativeRoot, ...operation.target.split("/")); +} +async function finalizeArchive(paths, journal, hooks) { + const events = await readNativeTransactionEvents(paths, journal.id); + if (events.some((event2) => event2.type === "archive-finalized")) return; + const finalizationStarted = events.some((event2) => event2.type === "archive-finalization-started"); + if (journal.schema === "comet.native.transaction.v2" && !finalizationStarted) { + const move = journal.operations.find((operation) => operation.id === "archive-change"); + if (!move || move.type !== "move" || !move.expectedSourceHash) { + throw new Error(`Archive transaction ${journal.id} has no content-bound archive move`); + } + const archiveContent = await inspectNativeArchiveContent( + archiveDirectoryFromJournal(paths, journal) + ); + if (archiveContent?.kind !== "directory" || archiveContent.hash !== move.expectedSourceHash) { + throw new Error( + `Native Archive content changed before finalization for transaction ${journal.id}` + ); + } + } + const archiveDir = archiveDirectoryFromJournal(paths, journal); + const stateFile = path34.join(archiveDir, NATIVE_CHANGE_STATE_FILE); + const state = await readNativeChangeFile(stateFile); + if (!journal.change || state.name !== journal.change) { + throw new Error(`Archive transaction ${journal.id} change mismatch`); + } + const run = await readNativeRunState(archiveDir); + if (!run || run.runId !== state.run_id || run.currentStep !== "archive" && !(run.currentStep === null && run.status === "completed")) { + throw new Error(`Native archive Run state is missing or inconsistent for ${state.name}`); + } + let completed = run; + if (run.currentStep === "archive") { + const decision = decideWithResolver( + NATIVE_RUNTIME_PACKAGE, + run, + /* @__PURE__ */ new Set(), + nativePhaseResolver, + void 0 + ); + if (!decision.action) throw new Error(decision.reason ?? "Native archive produced no action"); + completed = recordOutcomeWithResolver( + NATIVE_RUNTIME_PACKAGE, + decision.state, + { + actionId: decision.action.id, + status: "succeeded", + summary: `Archived Native change ${state.name}` + }, + nativePhaseResolver, + void 0 + ); + } + const evidenceHash = sha256Text(`archive:${journal.id}:${state.name}`); + const trajectory = await readNativeTrajectory(archiveDir, completed.trajectoryRef); + const transactionEvents = trajectory.filter((item) => item.data.transactionId === journal.id); + if (journal.schema === "comet.native.transaction.v2" && (transactionEvents.length > 1 || transactionEvents.some((item) => item.type !== "state_transitioned"))) { + throw new Error(`Native Archive trajectory has a transaction id collision: ${journal.id}`); + } + let event = transactionEvents.find((item) => item.type === "state_transitioned"); + const eventData = { + previousPhase: "archive", + nextPhase: null, + evidenceHash, + summary: `Archived Native change ${state.name}`, + transactionId: journal.id + }; + if (event && journal.schema === "comet.native.transaction.v2" && (!isDeepStrictEqual2(event.data, eventData) || event.runId !== completed.runId || event.timestamp !== journal.createdAt || event !== trajectory.at(-1))) { + throw new Error(`Native Archive trajectory event changed for transaction ${journal.id}`); + } + if (!finalizationStarted && (state.archived || run.currentStep === null || transactionEvents.length > 0)) { + throw new Error( + `Native Archive finalization state changed before its irreversible marker: ${journal.id}` + ); + } + if (!finalizationStarted) { + if (journal.schema === "comet.native.transaction.v2") { + await finalizeNativeArchiveTransactionV2(paths, journal, "archive-finalization-started"); + await hooks?.afterFinalizationStarted?.(journal); + } else { + await finalizeNativeTransaction(paths, journal, "archive-finalization-started"); + } + } + if (!state.archived) { + const updated = { ...state, archived: true }; + await writeNativeChangeFile(stateFile, updated); + } + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir: archiveDir, + run: completed, + type: "state_transitioned", + data: eventData, + ...journal.schema === "comet.native.transaction.v2" ? { now: new Date(journal.createdAt) } : {} + }); + } + await writeNativeCheckpoint2({ + changeDir: archiveDir, + run: completed, + trajectoryOffset: event.sequence, + evidenceHash, + ...journal.schema === "comet.native.transaction.v2" ? { now: new Date(journal.createdAt) } : {} + }); + await writeNativeRunState(archiveDir, completed); + await clearNativeSelectionIfLocked(paths, state.name); + if (journal.schema === "comet.native.transaction.v2") { + await finalizeNativeArchiveTransactionV2(paths, journal, "archive-finalized"); + } else { + await finalizeNativeTransaction(paths, journal, "archive-finalized"); + } +} +async function continueArchive(paths, journal, hooks) { + if (journal.schema === "comet.native.transaction.v2") { + const events = await readNativeTransactionEvents(paths, journal.id); + if (!events.some((event) => event.type === "operation-started")) { + const preflight = await inspectNativeArchivePreflight({ + paths, + name: journal.change, + now: new Date(journal.createdAt) + }); + if (!preflight.ready || preflight.preflightHash !== journal.preflightHash) { + throw new NativeArchivePreflightError( + preflight, + "Native Archive facts changed before the first transaction operation" + ); + } + } + const applied2 = await applyNativeArchiveTransactionV2(paths, journal, hooks); + await finalizeArchive(paths, applied2, hooks); + return finalizeNativeArchiveTransactionV2(paths, applied2, "commit"); + } + const applied = await applyNativeTransaction(paths, journal); + await finalizeArchive(paths, applied, hooks); + return finalizeNativeTransaction(paths, applied, "commit"); +} +function assertMatchingJournal(paths, journal) { + if (journal.kind !== "archive") throw new Error(`Transaction ${journal.id} is not an archive`); + if (journal.schema === "comet.native.transaction.v2") { + if (!journal.change) throw new Error(`Archive transaction ${journal.id} has no change`); + return; + } + if (path34.resolve(journal.projectRoot) !== path34.resolve(paths.projectRoot) || path34.resolve(journal.nativeRoot) !== path34.resolve(paths.nativeRoot)) { + throw new Error(`Transaction ${journal.id} belongs to a different Native root`); + } +} +async function archiveNativeChange(options) { + return withNativeMutationLock( + options.paths, + `archive ${options.name}`, + () => withNativeTransitionLock(options.paths, options.name, `archive ${options.name}`, async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const lock = await acquireNativeLock(options.paths, "archive", `archive ${options.name}`); + try { + if (!/^[a-f0-9]{64}$/u.test(options.expectedPreflightHash)) { + throw new Error("Native Archive expected preflight must be a SHA-256 hash"); + } + const now = options.now ?? /* @__PURE__ */ new Date(); + const preflight = await inspectNativeArchivePreflight({ + paths: options.paths, + name: options.name, + now + }); + if (!preflight.ready || preflight.preflightHash !== options.expectedPreflightHash) { + throw new NativeArchivePreflightError(preflight); + } + const state = await readNativeChange(options.paths, options.name); + assertArchiveReady(state); + await assertArchiveArtifacts(options.paths, state); + const transactionId = randomUUID6(); + const journal = await buildArchiveJournal({ + paths: options.paths, + state, + now, + transactionId, + preflight, + hooks: options.hooks + }); + await createNativeArchiveTransactionV2(options.paths, journal); + await options.hooks?.afterPrepared?.(journal); + await continueArchive(options.paths, journal, options.hooks); + return { + archiveDir: archiveDirectoryFromJournal(options.paths, journal), + transactionId, + preflightHash: preflight.preflightHash + }; + } finally { + await releaseNativeLock(lock); + } + }) + ); +} +async function recoverArchiveTransaction(options) { + return withNativeMutationLock( + options.paths, + `recover archive ${options.transactionId}`, + async () => { + const lock = await acquireNativeLock( + options.paths, + "archive", + `recover archive ${options.transactionId}` + ); + try { + const generic = await readNativeTransaction(options.paths, options.transactionId); + const journal = generic.schema === "comet.native.transaction.v2" ? await readNativeArchiveTransactionV2(options.paths, options.transactionId) : generic; + assertMatchingJournal(options.paths, journal); + if (journal.status === "committed" || journal.status === "rolled-back") return journal; + return options.strategy === "continue" ? continueArchive(options.paths, journal) : journal.schema === "comet.native.transaction.v2" ? rollbackNativeArchiveTransactionV2(options.paths, journal) : rollbackNativeTransaction(options.paths, journal); + } finally { + await releaseNativeLock(lock); + } + }, + { allowedTransactionId: options.transactionId } + ); +} + +// domains/comet-native/native-diagnostics.ts +import { promises as fs24 } from "fs"; + +// domains/comet-native/native-run-consistency.ts +import path35 from "path"; +function runPath(changeDir, ref) { + return path35.resolve(changeDir, ...ref.split(/[\\/]/u)); +} +async function inspectNativeRunConsistency(paths, state) { + const findings = []; + const changeDir = nativeChangeDir(paths, state.name); + const stateFile = runPath(changeDir, NATIVE_RUN_STORAGE.stateRef); + let run; + try { + run = await readNativeRunState(changeDir); + } catch (error) { + return [ + { + code: "run-state-invalid", + message: `Native Run state is invalid: ${error.message}`, + path: stateFile + } + ]; + } + if (!run) { + if (state.run_id !== null || state.phase !== "shape") { + findings.push({ + code: "run-state-missing", + message: "Native change references a missing Run state", + path: stateFile + }); + } + return findings; + } + if (state.run_id === null) { + return [ + { + code: "run-state-unexpected", + message: "Native change has a Run state but no run_id", + path: stateFile + } + ]; + } + if (run.runId !== state.run_id) { + findings.push({ + code: "run-id-mismatch", + message: `Native Run id ${run.runId} does not match change run_id ${state.run_id}`, + path: stateFile + }); + } + if (run.pending || run.status === "waiting") { + findings.push({ + code: "run-action-pending", + message: "Native Run has an unresolved pending action", + path: stateFile + }); + } + if (run.currentStep !== state.phase) { + findings.push({ + code: "run-phase-mismatch", + message: `Native Run step ${run.currentStep ?? "(none)"} does not match phase ${state.phase}`, + path: stateFile + }); + } + const trajectoryFile2 = runPath(changeDir, run.trajectoryRef); + const tailInspection = await inspectNativeTrajectoryTail(paths, state.name); + if (tailInspection.status === "repairable") { + findings.push({ + code: "trajectory-tail-incomplete", + message: `Native trajectory final line is incomplete at line ${tailInspection.line}; doctor repair can discard ${tailInspection.discardedBytes} incomplete byte(s)`, + path: trajectoryFile2 + }); + return findings; + } + if (tailInspection.status === "invalid") { + findings.push({ + code: "trajectory-invalid", + message: `Native trajectory is invalid at line ${tailInspection.line}: ${tailInspection.message}`, + path: trajectoryFile2 + }); + return findings; + } + let trajectory; + try { + trajectory = await readNativeTrajectory(changeDir, run.trajectoryRef); + if (trajectory.length === 0 || trajectory.some( + (event, index) => !event || typeof event !== "object" || event.sequence !== index + 1 || event.runId !== run.runId || typeof event.type !== "string" || !event.data || typeof event.data !== "object" || Array.isArray(event.data) + )) { + throw new Error("trajectory events are missing or inconsistent"); + } + } catch (error) { + findings.push({ + code: "trajectory-invalid", + message: `Native trajectory is invalid: ${error.message}`, + path: trajectoryFile2 + }); + return findings; + } + const checkpointFile = runPath(changeDir, run.checkpointRef); + try { + const checkpoint = await readNativeCheckpoint(changeDir, run.checkpointRef); + if (!checkpoint) { + findings.push({ + code: "checkpoint-missing", + message: "Native Run checkpoint is missing", + path: checkpointFile + }); + } else if (checkpoint.runId !== run.runId || checkpoint.stateVersion !== run.iteration || checkpoint.trajectoryOffset !== trajectory.length) { + findings.push({ + code: "checkpoint-mismatch", + message: "Native Run checkpoint does not match Run state and trajectory", + path: checkpointFile + }); + } + } catch (error) { + findings.push({ + code: "checkpoint-invalid", + message: `Native Run checkpoint is invalid: ${error.message}`, + path: checkpointFile + }); + } + return findings; +} + +// domains/comet-native/native-continuation.ts +var REPAIR_CODES = /^(?:run-|trajectory-|checkpoint-(?:missing|mismatch|invalid|progress-invalid)|transition-(?:incomplete|invalid))/u; +function requiredPhaseInputs(state) { + if (state.phase === "shape") { + return ["summary", "shared-understanding-confirmation"]; + } + if (state.phase === "build") { + return state.approval === "confirmed" ? ["summary", "artifact-or-no-code-reason"] : ["summary", "artifact-or-no-code-reason", "shared-understanding-confirmation"]; + } + if (state.phase === "verify") return ["summary", "verification-result", "verification-report"]; + return []; +} +function nativeContinuation(options) { + const findings = options.findings ?? []; + const actionableFindings = findings.filter( + (finding) => !isNativeWorkspaceAdvisoryCode(finding.code) + ); + const decision = actionableFindings.find((finding) => finding.requiresUserDecision); + const repair = actionableFindings.find( + (finding) => finding.repairCommand !== null || REPAIR_CODES.test(finding.code) + ); + const stagnationStop = actionableFindings.find( + (finding) => finding.code === "repair-stagnation-stop" + ); + const requiredInputs = [ + ...new Set(actionableFindings.map((finding) => finding.requiredAction)) + ].sort(); + if (options.done) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "done", + action: "none", + command: null, + requiresUserDecision: false, + requiredInputs: [] + }; + } + if (decision) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "await-user", + action: "work-phase", + command: null, + requiresUserDecision: true, + requiredInputs + }; + } + if (stagnationStop) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "work-phase", + command: null, + requiresUserDecision: false, + requiredInputs: ["implementation-progress-or-repair-override"] + }; + } + if (repair) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "repair", + command: repair.repairCommand, + requiresUserDecision: false, + requiredInputs + }; + } + if (options.evidenceRetreat) { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "advance-phase", + command: `comet native next ${options.state.name} --summary ""`, + requiresUserDecision: false, + requiredInputs: ["summary"] + }; + } + if (actionableFindings.length > 0) { + if (options.state.phase === "archive") { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "blocked", + action: "none", + command: null, + requiresUserDecision: false, + requiredInputs + }; + } + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "work-phase", + command: null, + requiresUserDecision: false, + requiredInputs + }; + } + if (options.state.phase === "archive") { + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: options.archiveReady ? "continue" : "blocked", + action: options.archiveReady ? "archive" : "none", + command: options.archiveReady ? `comet native archive ${options.state.name} --dry-run` : null, + requiresUserDecision: false, + requiredInputs: options.archiveReady ? [] : ["archive-readiness"] + }; + } + const confirmationSuffix = options.state.phase === "shape" || options.state.phase === "build" && options.state.approval !== "confirmed" ? " --confirmed" : ""; + return { + schema: "comet.native.continuation.v1", + skill: "comet-native", + change: options.state.name, + phase: options.state.phase, + revision: options.state.revision, + disposition: "continue", + action: "advance-phase", + command: `comet native next ${options.state.name} --summary ""${confirmationSuffix}`, + requiresUserDecision: false, + requiredInputs: requiredPhaseInputs(options.state) + }; +} + +// domains/comet-native/native-findings.ts +import path36 from "path"; +var FINDING_SUMMARY_CODE_BUDGET = 8; +var EXACT_METADATA = { + "brief-blocking-question": { + severity: "error", + requiredAction: "answer-blocking-question", + retry: "next", + repair: "none" + }, + "shape-confirmation-required": { + severity: "error", + requiredAction: "confirm-shared-understanding", + retry: "next", + repair: "none" + }, + "approval-confirmation-required": { + severity: "error", + requiredAction: "confirm-shared-understanding", + retry: "next", + repair: "none" + }, + "transition-incomplete": { + severity: "error", + requiredAction: "recover-transition", + retry: "status", + repair: "doctor" + }, + "trajectory-tail-incomplete": { + severity: "error", + requiredAction: "repair-trajectory-tail", + retry: "status", + repair: "doctor" + }, + "checkpoint-progress-invalid": { + severity: "error", + requiredAction: "manually-isolate-invalid-checkpoint", + retry: "none", + repair: "none" + }, + "checkpoint-progress-incomplete": { + severity: "error", + requiredAction: "recover-progress-checkpoint", + retry: "status", + repair: "doctor" + }, + "checkpoint-manifest-invalid": { + severity: "error", + requiredAction: "record-checkpoint-again", + retry: "status", + repair: "none" + }, + "verification-scope-partial": { + severity: "error", + requiredAction: "confirm-partial-verification-scope", + retry: "next", + repair: "none" + }, + "native-change-conflict": { + severity: "error", + requiredAction: "resolve-native-change-conflict", + retry: "status", + repair: "none" + }, + "native-change-overlap": { + severity: "error", + requiredAction: "inspect-native-change-overlap", + retry: "status", + repair: "none" + }, + "contract-changed-after-approval": { + severity: "error", + requiredAction: "re-confirm-contract", + retry: "next", + repair: "none" + }, + "baseline-snapshot-incomplete": { + severity: "error", + requiredAction: "resolve-native-baseline", + retry: "none", + repair: "none" + }, + "baseline-snapshot-missing": { + severity: "error", + requiredAction: "resolve-native-baseline", + retry: "none", + repair: "none" + }, + "workspace-inspection-unavailable": { + severity: "info", + requiredAction: "migrate-workspace-identity", + retry: "status", + repair: "doctor" + }, + "repair-stagnation-warning": { + severity: "warning", + requiredAction: "change-repair-approach", + retry: "next", + repair: "none" + }, + "repair-stagnation-stop": { + severity: "error", + requiredAction: "make-progress-or-explicitly-override-repair", + retry: "none", + repair: "none" + }, + "repair-iteration-limit": { + severity: "error", + requiredAction: "change-implementation-before-starting-a-new-repair-episode", + retry: "next", + repair: "none" + }, + "repair-override-exhausted": { + severity: "error", + requiredAction: "review-repeated-failure-after-override", + retry: "none", + repair: "none" + } +}; +function inferredMetadata(code) { + const exact = EXACT_METADATA[code]; + if (exact) return exact; + if (/^(?:run-|trajectory-|checkpoint-(?:missing|mismatch|invalid)|transition-invalid)/u.test(code)) { + return { + severity: "error", + requiredAction: "isolate-or-restore-native-runtime-from-a-trusted-copy", + retry: "none", + repair: "none" + }; + } + if (code.startsWith("brief-")) { + return { + severity: "error", + requiredAction: "complete-brief", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("spec-")) { + return { + severity: "error", + requiredAction: "resolve-spec-state", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("verification-")) { + return { + severity: "error", + requiredAction: "complete-verification-evidence", + retry: "next", + repair: "none" + }; + } + if (code.startsWith("build-")) { + return { + severity: "error", + requiredAction: "record-build-evidence", + retry: "next", + repair: "none" + }; + } + if (isNativeWorkspaceAdvisoryCode(code)) { + return { + severity: code === "workspace-inspection-unavailable" ? "info" : "warning", + requiredAction: "inspect-workspace-advisory", + retry: "status", + repair: "none" + }; + } + return { + severity: "error", + requiredAction: "resolve-finding", + retry: "status", + repair: "none" + }; +} +function projectRelativePath3(paths, state, finding) { + if (!finding.path) return null; + let target; + if (path36.isAbsolute(finding.path)) { + target = path36.resolve(finding.path); + } else if (/^(?:brief-|verification-|spec-source)/u.test(finding.code)) { + target = path36.resolve(nativeChangeDir(paths, state.name), ...finding.path.split(/[\\/]/u)); + } else { + target = path36.resolve(paths.projectRoot, ...finding.path.split(/[\\/]/u)); + } + if (!isInsidePath(paths.projectRoot, target)) return null; + const relative = path36.relative(paths.projectRoot, target).replaceAll("\\", "/"); + return relative === "" ? "." : relative; +} +function retryCommand(retry, state, code) { + if (retry === "next") { + return `comet native next ${state.name} --summary ""${code === "contract-changed-after-approval" || code === "shape-confirmation-required" || code === "approval-confirmation-required" ? " --confirmed" : ""}`; + } + if (retry === "status") return `comet native status ${state.name} --details`; + return null; +} +function structureNativeFindings(options) { + return options.findings.map((finding) => { + const metadata = inferredMetadata(finding.code); + return { + code: finding.code, + message: finding.message, + severity: metadata.severity, + path: projectRelativePath3(options.paths, options.state, finding), + requiredAction: metadata.requiredAction, + retryCommand: retryCommand(metadata.retry, options.state, finding.code), + repairCommand: metadata.repair === "doctor" ? `comet native doctor ${options.state.name} --repair${finding.code.startsWith("transition-") ? " --strategy continue" : ""}` : null, + // This is intentionally code-based, not severity-based. Model-actionable + // missing data must never be presented as a user decision. + requiresUserDecision: finding.code === "brief-blocking-question" || finding.code === "shape-confirmation-required" || finding.code === "approval-confirmation-required" || finding.code === "contract-changed-after-approval" || finding.code === "verification-scope-partial" || finding.code === "repair-iteration-limit" || finding.code === "repair-override-exhausted" + }; + }).sort((left, right) => { + const severityRank = { error: 0, warning: 1, info: 2 }; + return severityRank[left.severity] - severityRank[right.severity] || left.code.localeCompare(right.code) || (left.path ?? "").localeCompare(right.path ?? "") || left.message.localeCompare(right.message); + }); +} +function summarizeNativeFindings(findings) { + const codes = [...new Set(findings.map((finding) => finding.code))]; + return { + total: findings.length, + errors: findings.filter((finding) => finding.severity === "error").length, + warnings: findings.filter((finding) => finding.severity === "warning").length, + info: findings.filter((finding) => finding.severity === "info").length, + requiresUserDecision: findings.some((finding) => finding.requiresUserDecision), + codes: codes.slice(0, FINDING_SUMMARY_CODE_BUDGET), + truncated: codes.length > FINDING_SUMMARY_CODE_BUDGET + }; +} + +// domains/comet-native/native-resume-view.ts +import path37 from "path"; +var COMPACT_TEXT_BUDGET = 240; +var COMPACT_REASON_CODE_BUDGET = 8; +var NATIVE_INSPECTION_REASON_DETAIL_BUDGET = 50; +function compactText(value) { + const characters = Array.from(value); + return characters.length <= COMPACT_TEXT_BUDGET ? value : `${characters.slice(0, COMPACT_TEXT_BUDGET - 1).join("")}…`; +} +function reasonCode(reason) { + const separator = reason.indexOf(":"); + return separator < 0 ? reason : reason.slice(0, separator); +} +function inspectionViews(reasons) { + const codes = [...new Set(reasons.map(reasonCode))]; + const inspection = { + freshness: reasons.length === 0 || reasons.length === 1 && reasons[0] === "no-checkpoint" ? "fresh" : "stale", + codes: codes.slice(0, COMPACT_REASON_CODE_BUDGET), + reasonCount: reasons.length, + codesTruncated: codes.length > COMPACT_REASON_CODE_BUDGET + }; + return { + inspection, + inspectionDetails: { + ...inspection, + reasons: reasons.slice(0, NATIVE_INSPECTION_REASON_DETAIL_BUDGET), + reasonsTruncated: reasons.length > NATIVE_INSPECTION_REASON_DETAIL_BUDGET + } + }; +} +async function buildNativeResumeView(options) { + let pendingFinding = null; + try { + const pending = await readNativeCheckpointJournal(options.paths, options.state.name); + if (pending) { + pendingFinding = { + code: "checkpoint-progress-incomplete", + message: `Native progress checkpoint ${pending.id} requires deterministic recovery`, + path: nativeCheckpointJournalFile(options.paths, options.state.name) + }; + } + } catch (error) { + pendingFinding = { + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint journal is invalid: ${error.message}. Automatic repair is unavailable; inspect and move the invalid checkpoint journal aside before retrying`, + path: nativeCheckpointJournalFile(options.paths, options.state.name) + }; + } + const inspected = await inspectNativeCheckpointFreshness({ + paths: options.paths, + name: options.state.name, + stateRevision: options.state.revision + }); + const allReasons = pendingFinding ? [pendingFinding.code, ...inspected.reasons] : inspected.reasons; + const views = inspectionViews(allReasons); + if (!inspected.checkpoint) { + return { + inspection: views.inspection, + inspectionDetails: views.inspectionDetails, + checkpoint: null, + checkpointDetails: null, + findings: pendingFinding ? [pendingFinding, ...inspected.findings] : inspected.findings, + maxCheckpointArtifacts: NATIVE_CHECKPOINT_LIMITS.maxArtifacts + }; + } + const compact = { + id: inspected.checkpoint.id, + createdAt: inspected.checkpoint.createdAt, + phase: inspected.checkpoint.phase, + stateRevision: inspected.checkpoint.stateRevision, + summary: compactText(inspected.checkpoint.summary), + nextAction: compactText(inspected.checkpoint.nextAction), + artifactCount: inspected.checkpoint.artifactCount + }; + const details = inspected.manifest ? { + ...compact, + summary: inspected.checkpoint.summary, + nextAction: inspected.checkpoint.nextAction, + manifestHash: inspected.checkpoint.manifestHash, + manifestRef: path37.relative( + options.paths.projectRoot, + path37.join( + nativeChangeDir(options.paths, options.state.name), + ...inspected.checkpoint.manifestRef.split("/") + ) + ).replaceAll("\\", "/"), + artifacts: inspected.manifest.artifacts, + totalBytes: inspected.manifest.totalBytes + } : null; + return { + inspection: views.inspection, + inspectionDetails: views.inspectionDetails, + checkpoint: compact, + checkpointDetails: details, + findings: pendingFinding ? [pendingFinding, ...inspected.findings] : inspected.findings, + maxCheckpointArtifacts: NATIVE_CHECKPOINT_LIMITS.maxArtifacts + }; +} + +// domains/comet-native/native-repair-integration.ts +function assertRepairableBuildState(state) { + if (state.phase !== "build" || state.verification_result !== "fail" || !state.implementation_scope || !state.verification_evidence) { + throw new Error("Native repair guard requires a failed Verify-to-Build state"); + } +} +async function readNativeCommittedRepairTrajectory(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const run = await readNativeRunState(changeDir); + if (!run || !state.run_id || run.runId !== state.run_id) { + throw new Error("Native repair history Run state is missing or mismatched"); + } + const checkpoint = await readNativeCheckpoint(changeDir, run.checkpointRef); + if (!checkpoint || checkpoint.runId !== run.runId || checkpoint.stateVersion !== run.iteration) { + throw new Error("Native repair history checkpoint is missing or mismatched"); + } + return { + trajectory: await readNativeTrajectory(changeDir, run.trajectoryRef), + committedTrajectoryOffset: checkpoint.trajectoryOffset, + runId: run.runId + }; +} +async function inspectNativeRepairHistory(paths, state) { + const committed = await readNativeCommittedRepairTrajectory(paths, state); + return { + committed, + latest: inspectLatestNativeRepairProjection(committed), + history: rebuildNativeRepairHistory(committed) + }; +} +function decisionFromInspection(inspection) { + const latest = inspection.latest; + if (!latest) return null; + const failures = inspection.history.filter((entry2) => entry2.kind === "failure"); + let consecutiveFailures = 0; + for (let index = failures.length - 1; index >= 0; index -= 1) { + if (failures[index].signatureHash !== latest.signatureHash) break; + consecutiveFailures += 1; + } + const overrideAccepted = latest.overrideSummaryHash !== null; + const overrideAlreadyUsed = inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ); + return { + disposition: latest.disposition, + reasonCode: overrideAccepted ? "override-accepted" : overrideAlreadyUsed && latest.disposition === "manual-stop" ? "override-already-used" : latest.disposition === "hard-stop" ? "repair-iteration-limit" : latest.disposition === "manual-stop" ? "repeated-failure-stop" : latest.disposition === "warn" ? "repeated-failure-warning" : "new-failure-signature", + signatureHash: latest.signatureHash, + consecutiveFailures, + totalRepairFailures: failures.length, + remainingIterations: Math.max( + 0, + NATIVE_REPAIR_STAGNATION_LIMITS.maxRepairIterations - failures.length + ), + overrideAccepted + }; +} +async function inspectLatestNativeRepairDecision(paths, state) { + return decisionFromInspection(await inspectNativeRepairHistory(paths, state)); +} +function projectNativeRepairDecision(result2) { + return { + disposition: result2.decision.disposition, + reasonCode: result2.decision.reasonCode, + signatureHash: result2.decision.signature.signatureHash, + consecutiveFailures: result2.decision.consecutiveFailures, + totalRepairFailures: result2.decision.totalRepairFailures, + remainingIterations: result2.decision.remainingIterations, + overrideAccepted: result2.decision.overrideAccepted + }; +} +async function inspectNativeRepairFailureForTransition(options) { + if (!options.state.implementation_scope) { + throw new Error("Native repair failure has no implementation scope"); + } + const [committed, implementationScope] = await Promise.all([ + readNativeCommittedRepairTrajectory(options.paths, options.state), + readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ) + ]); + return inspectNativeRepairFailure({ + ...committed, + envelope: options.envelope, + implementationScope, + ...options.categories ? { categories: options.categories } : {}, + ...options.failedCheckIds ? { failedCheckIds: options.failedCheckIds } : {} + }); +} +async function inspectNativeRepairBuildGuard(options) { + const inspection = await inspectNativeRepairHistory(options.paths, options.state); + const latest = inspection.latest; + if (!latest || latest.disposition !== "manual-stop" && latest.disposition !== "hard-stop") { + if (options.override) { + throw new Error("Native repair override requires the latest manual stop"); + } + return { disposition: "proceed", eventProjection: null }; + } + const overrideRecorded = inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ); + const activeFailedRepair = options.state.phase === "build" && options.state.verification_result === "fail" && options.state.implementation_scope !== null && options.state.verification_evidence !== null; + if (!activeFailedRepair) { + if (options.override) { + throw new Error("Native repair override requires an active failed Verify-to-Build state"); + } + return { disposition: "proceed", eventProjection: null }; + } + assertRepairableBuildState(options.state); + const [previousEnvelope, previousImplementationScope] = await Promise.all([ + readNativeVerificationEvidence( + options.paths, + options.state.name, + options.state.verification_evidence + ), + readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ) + ]); + if (previousImplementationScope.scope.scopeHash !== previousEnvelope.implementationScopeHash) { + throw new Error("Native repair verification evidence does not match its implementation scope"); + } + if (nativeRepairScopeHash(options.currentImplementationScope) !== nativeRepairScopeHash(previousImplementationScope)) { + if (options.override) { + throw new Error("Native repair override is not valid after implementation scope progress"); + } + return { disposition: "proceed", eventProjection: null }; + } + if (latest.disposition === "hard-stop") { + if (options.override) { + throw new Error( + "Native repair hard stop cannot be overridden without implementation progress" + ); + } + return { + disposition: "hard-stop", + signatureHash: latest.signatureHash, + overrideRecorded + }; + } + if (overrideRecorded) { + return { + disposition: "hard-stop", + signatureHash: latest.signatureHash, + overrideRecorded + }; + } + if (!options.override) { + return { + disposition: "manual-stop", + signatureHash: latest.signatureHash, + overrideRecorded: false + }; + } + return { + disposition: "proceed", + eventProjection: acceptLatestNativeRepairOverride({ + ...inspection.committed, + override: options.override + }).eventProjection + }; +} +async function inspectNativeRepairStatus(paths, state) { + if (state.phase !== "build" || state.verification_result !== "fail") return null; + const inspection = await inspectNativeRepairHistory(paths, state); + const latest = inspection.latest; + if (!latest || latest.disposition !== "manual-stop" && latest.disposition !== "hard-stop") { + return null; + } + return { + disposition: latest.disposition, + signatureHash: latest.signatureHash, + overrideRecorded: inspection.history.some( + (entry2) => entry2.kind === "override" && entry2.signatureHash === latest.signatureHash + ) + }; +} + +// domains/comet-native/native-diagnostics.ts +var NATIVE_STATUS_CURSOR_PATTERN = /^native-status-v1\.([a-f0-9]{64})\.([0-9a-z]+)\.([a-f0-9]{64})$/u; +var NATIVE_STATUS_PAGE_LIMITS = Object.freeze({ + maxItems: 24, + maxChanges: 4096, + maxSerializedBytes: 512 * 1024 +}); +async function selectedName(paths) { + try { + return (await readNativeSelectionRecord(paths))?.change ?? null; + } catch { + return null; + } +} +function nativeNextCommand(state, archiveReady, evidenceRetreat = false, _clarificationMode) { + if (state.phase === "archive") { + return archiveReady ? `comet native archive ${state.name} --dry-run` : evidenceRetreat ? `comet native next ${state.name} --summary ""` : null; + } + return `comet native next ${state.name} --summary ""${state.phase === "shape" || state.phase === "build" && state.approval !== "confirmed" ? " --confirmed" : ""}`; +} +async function statusFindings(paths, state) { + const changeDir = nativeChangeDir(paths, state.name); + const findings = [ + ...(await validateNativeBrief(changeDir, state.brief)).findings, + ...(await validateNativeSpecChanges(paths, state)).findings, + ...await inspectNativeRunConsistency(paths, state) + ]; + if (state.phase === "shape" || state.phase === "build") { + try { + const capturedBaseline = await readNativeBaselineManifest(paths, state.name); + if (capturedBaseline === null) { + findings.push({ + code: "baseline-snapshot-missing", + message: "Native baseline is missing; restore a trusted baseline before advancing" + }); + } else { + const baseline = await filterNativeContentSnapshotToProjectScope(paths, capturedBaseline); + if (!baseline.complete) { + findings.push({ + code: "baseline-snapshot-incomplete", + message: `Native baseline is incomplete within the project-owned scope (${baseline.omittedCount} omitted entries); resolve the omissions before advancing` + }); + } + } + } catch (error) { + findings.push({ + code: "baseline-snapshot-invalid", + message: `Native baseline could not be inspected safely: ${error.message}` + }); + } + } + if (state.phase === "build") { + try { + const current = await collectNativeContractFiles({ + changeDir, + briefRef: state.brief, + specChanges: state.spec_changes + }); + if ((state.approved_contract_hash ?? null) !== current.contract.contractHash) { + findings.push({ + code: "contract-changed-after-approval", + message: state.approval === null || state.approved_contract_hash === null || state.approved_contract_hash === void 0 ? "Native approval is not bound to a contract hash; re-confirm the current contract" : "Native contract changed after approval; re-confirm the current contract" + }); + } + } catch (error) { + findings.push({ + code: "contract-inspection-invalid", + message: `Native approved contract could not be inspected safely: ${error.message}` + }); + } + } + try { + if (await inspectPendingNativeTransition(paths, state.name)) { + findings.unshift({ + code: "transition-incomplete", + message: "Native phase transition recovery is pending" + }); + } + } catch (error) { + findings.unshift({ + code: "transition-invalid", + message: `Native transition journal is invalid: ${error.message}` + }); + } + if (state.verification_report) { + findings.push( + ...(await validateNativeVerification(changeDir, state.verification_report)).findings + ); + } else if (state.phase === "verify" || state.phase === "archive" || state.verification_result === "pass") { + findings.push({ + code: "verification-report-missing", + message: "Native change has no verification report" + }); + } + return findings; +} +async function inspectNativeStatus(paths, name, options) { + const selected = await selectedName(paths) === name; + let state; + try { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required" && inspection.state) { + return { + name, + phase: inspection.state.phase, + revision: "revision" in inspection.state ? inspection.state.revision : null, + approval: inspection.state.approval, + verificationResult: inspection.state.verification_result, + specChanges: inspection.state.spec_changes.length, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["migration-required"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + schema: inspection.schema, + migrationRequired: true, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + error: inspection.message + }; + } + if (inspection.status !== "current" || !inspection.state) { + return { + name, + phase: "invalid", + revision: null, + approval: null, + verificationResult: "pending", + specChanges: 0, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["runtime-incompatible"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + error: inspection.message ?? `Native change ${name} is incompatible` + }; + } + state = inspection.state; + } catch (error) { + return { + name, + phase: "invalid", + revision: null, + approval: null, + verificationResult: "pending", + specChanges: 0, + selected, + nextCommand: null, + archiveReady: false, + inspection: { + freshness: "stale", + codes: ["change-invalid"], + reasonCount: 1, + codesTruncated: false + }, + findingSummary: { + total: 0, + errors: 0, + warnings: 0, + info: 0, + requiresUserDecision: false, + codes: [], + truncated: false + }, + detailsCommand: `comet native status ${name} --details`, + checkpoint: null, + continuation: null, + error: error.message + }; + } + const resume = await buildNativeResumeView({ paths, state }); + let acceptancePage; + if (options?.details && (state.phase === "verify" || state.phase === "archive")) { + try { + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(paths, state.name), + briefRef: state.brief, + specChanges: state.spec_changes + }); + acceptancePage = projectNativeAcceptancePage({ + criteria: contract.contract.acceptance, + acceptanceHash: contract.contract.acceptanceHash, + ...options.acceptanceCursor ? { cursor: options.acceptanceCursor } : {} + }); + } catch (error) { + if (options.acceptanceCursor) throw error; + acceptancePage = void 0; + } + } + const conflictFindings = []; + try { + const conflicts = await inspectNativeChangeConflicts(paths, state.name); + conflictFindings.push( + ...conflicts.findingCodes.map((code) => ({ + code, + message: `Native change overlap is visible in the current root: ${code}` + })) + ); + } catch { + conflictFindings.push({ + code: "native-conflict-inspection-invalid", + message: "Native change overlap could not be recomputed safely" + }); + } + const workspaceFindings = []; + try { + const identity = await readNativeWorkspaceIdentity(paths, state.name); + if (identity) { + const workspace = await inspectNativeWorkspaceAdvisory({ + paths, + identity + }); + workspaceFindings.push( + ...workspace.findingCodes.map((code) => ({ + code, + message: `Native workspace advisory changed: ${code} (${workspace.driftComponents.join(", ") || "no-component"})` + })) + ); + } + } catch { + workspaceFindings.push({ + code: "workspace-inspection-unavailable", + message: "Native workspace advisory could not be recomputed safely" + }); + } + const verifyScopeFindings = []; + let verifyEvidenceRetreat = false; + if (state.phase === "verify") { + const freshness = await inspectNativeImplementationScopeFreshness({ paths, state }); + verifyEvidenceRetreat = freshness.freshness !== "fresh"; + verifyScopeFindings.push( + ...freshness.findingCodes.map((code) => ({ + code, + message: `Native Verify implementation scope is not current: ${code}` + })) + ); + } + let repair = null; + const repairFindings = []; + if (state.phase === "build" && state.verification_result === "fail") { + try { + repair = await inspectNativeRepairStatus(paths, state); + if (repair) { + const code = repair.disposition === "hard-stop" ? "repair-iteration-limit" : repair.overrideRecorded ? "repair-override-exhausted" : "repair-stagnation-stop"; + repairFindings.push({ + code, + message: `Native repair is stopped at failure signature: ${repair.signatureHash}` + }); + } + } catch { + repairFindings.push({ + code: "trajectory-invalid", + message: "Native repair history could not be reconstructed safely" + }); + } + } + let archivePreflight = null; + const archiveFindings = []; + if (state.phase === "archive") { + try { + archivePreflight = await inspectNativeArchivePreflight({ paths, name: state.name }); + archiveFindings.push( + ...archivePreflight.findingCodes.map((code) => ({ + code, + message: `Native Archive is blocked: ${code}` + })) + ); + } catch { + archiveFindings.push({ + code: "archive-preflight-invalid", + message: "Native Archive preflight could not be recomputed safely" + }); + } + } + const rawFindings = [ + ...await statusFindings(paths, state), + ...resume.findings, + ...conflictFindings, + ...workspaceFindings, + ...verifyScopeFindings, + ...repairFindings, + ...archiveFindings + ].filter( + (finding, index, values) => values.findIndex( + (candidate) => candidate.code === finding.code && candidate.path === finding.path + ) === index + ); + const findings = structureNativeFindings({ paths, state, findings: rawFindings }); + const archiveBlockingFindings = findings.filter( + (finding) => !isNativeWorkspaceAdvisoryCode(finding.code) + ); + const archiveReady = state.phase === "archive" && archivePreflight?.ready === true && archiveBlockingFindings.length === 0; + const evidenceRetreat = verifyEvidenceRetreat || state.phase === "archive" && (archivePreflight?.findingCodes ?? []).some( + (code) => (/* @__PURE__ */ new Set([ + "verification-evidence-stale", + "verification-evidence-invalid", + "verification-evidence-missing", + "verification-contract-stale", + "verification-implementation-stale", + "verification-report-stale", + "verification-state-mismatch" + ])).has(code) + ); + const mutationBlocked = findings.some( + (finding) => finding.code === "trajectory-tail-incomplete" || finding.code === "trajectory-invalid" + ); + const repairBlocked = repair !== null; + const firstErrorFinding = findings.find((finding) => finding.severity === "error"); + return { + name: state.name, + phase: state.phase, + revision: state.revision, + approval: state.approval, + verificationResult: state.verification_result, + specChanges: state.spec_changes.length, + selected, + nextCommand: mutationBlocked || repairBlocked ? null : nativeNextCommand(state, archiveReady, evidenceRetreat, options?.clarificationMode), + archiveReady, + inspection: resume.inspection, + findingSummary: summarizeNativeFindings(findings), + detailsCommand: `comet native status ${state.name} --details`, + checkpoint: resume.checkpoint, + continuation: nativeContinuation({ + state, + findings, + archiveReady, + evidenceRetreat, + clarificationMode: options?.clarificationMode + }), + repair, + ...options?.details ? { + ...acceptancePage ? { acceptancePage } : {}, + findings: findings.slice(0, 50), + inspectionDetails: resume.inspectionDetails, + checkpointDetails: resume.checkpointDetails, + budgets: { + maxFindings: 50, + maxInspectionReasons: NATIVE_INSPECTION_REASON_DETAIL_BUDGET, + maxCheckpointArtifacts: resume.maxCheckpointArtifacts, + findingsTruncated: findings.length > 50, + inspectionReasonsTruncated: resume.inspectionDetails.reasonsTruncated, + checkpointArtifactsTruncated: false + } + } : {}, + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + ...firstErrorFinding ? { error: firstErrorFinding.message } : {} + }; +} +async function boundedNativeChangeNames(paths) { + let guard; + try { + guard = await captureNativeProtectedDirectoryGuard({ + root: paths.nativeRoot, + directory: paths.changesDir, + label: "Native status changes directory" + }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const names = []; + const directory = await fs24.opendir(paths.changesDir); + try { + for await (const entry2 of directory) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + names.push(entry2.name); + if (names.length > NATIVE_STATUS_PAGE_LIMITS.maxChanges) { + throw new Error( + `Native status exceeds ${NATIVE_STATUS_PAGE_LIMITS.maxChanges} visible changes` + ); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + await guard.verify(); + return names.sort(); +} +function nativeStatusCursor(namesHash, offset) { + const encodedOffset = offset.toString(36); + const integrity = canonicalHash("comet.native.status-cursor.v1", { namesHash, offset }); + return `native-status-v1.${namesHash}.${encodedOffset}.${integrity}`; +} +function nativeStatusOffset(options) { + if (options.cursor === void 0 || options.cursor === null) return 0; + const match = NATIVE_STATUS_CURSOR_PATTERN.exec(options.cursor); + if (!match) throw new Error("Native status cursor is invalid"); + if (match[1] !== options.namesHash) throw new Error("Native status cursor is stale"); + const offset = Number.parseInt(match[2], 36); + if (!Number.isSafeInteger(offset) || offset <= 0 || offset >= options.total || offset.toString(36) !== match[2]) { + throw new Error("Native status cursor offset is invalid"); + } + const expected = canonicalHash("comet.native.status-cursor.v1", { + namesHash: options.namesHash, + offset + }); + if (match[3] !== expected) throw new Error("Native status cursor integrity check failed"); + return offset; +} +async function listNativeStatusPage(paths, options) { + const names = await boundedNativeChangeNames(paths); + const namesHash = canonicalHash("comet.native.status-names.v1", names); + const offset = nativeStatusOffset({ + namesHash, + total: names.length, + cursor: options?.cursor + }); + const candidates = await Promise.all( + names.slice(offset, offset + NATIVE_STATUS_PAGE_LIMITS.maxItems).map( + (name) => inspectNativeStatus(paths, name, { + clarificationMode: options?.clarificationMode + }) + ) + ); + const items = []; + for (const candidate of candidates) { + const trialItems = [...items, candidate]; + const nextOffset2 = offset + trialItems.length; + const trial = { + schema: "comet.native.status-page.v1", + total: names.length, + offset, + items: trialItems, + nextCursor: nextOffset2 < names.length ? nativeStatusCursor(namesHash, nextOffset2) : null, + limits: { ...NATIVE_STATUS_PAGE_LIMITS } + }; + if (Buffer.byteLength(JSON.stringify(trial), "utf8") > NATIVE_STATUS_PAGE_LIMITS.maxSerializedBytes) { + if (items.length === 0) { + throw new Error("Native status item exceeds its page serialization budget"); + } + break; + } + items.push(candidate); + } + const nextOffset = offset + items.length; + return { + schema: "comet.native.status-page.v1", + total: names.length, + offset, + items, + nextCursor: nextOffset < names.length ? nativeStatusCursor(namesHash, nextOffset) : null, + limits: { ...NATIVE_STATUS_PAGE_LIMITS } + }; +} +async function listNativeStatus(paths, options) { + return (await listNativeStatusPage(paths, { + clarificationMode: options?.clarificationMode + })).items; +} + +// domains/comet-native/native-doctor.ts +import { promises as fs28 } from "fs"; +import path41 from "path"; + +// domains/comet-native/native-evidence-retention.ts +import { createHash as createHash13, randomUUID as randomUUID8 } from "node:crypto"; +import { constants as fsConstants5, promises as fs26 } from "node:fs"; +import path39 from "node:path"; + +// domains/comet-native/native-schema-migration.ts +var import_yaml4 = __toESM(require_dist(), 1); +import { randomUUID as randomUUID7 } from "crypto"; +import { promises as fs25 } from "fs"; +import path38 from "path"; +import { isDeepStrictEqual as isDeepStrictEqual3 } from "util"; +var HASH_PATTERN14 = /^[a-f0-9]{64}$/u; +var MIGRATION_JOURNAL_KEYS = /* @__PURE__ */ new Set([ + "schema", + "id", + "change", + "fromSchema", + "toSchema", + "sourceHash", + "targetHash", + "createdAt", + "nextState", + "transition", + "transitionSupersede", + "runRetreat" +]); +var MIGRATION_TRANSITION_KEYS = /* @__PURE__ */ new Set(["sourceHash", "targetHash", "nextJournal"]); +var MIGRATION_TRANSITION_SUPERSEDE_KEYS = /* @__PURE__ */ new Set([ + "sourceHash", + "transitionId", + "previousRun", + "nextRun", + "evidenceHash", + "eventData" +]); +var MIGRATION_SUPERSEDE_EVENT_KEYS = /* @__PURE__ */ new Set([ + "fromSchema", + "toSchema", + "previousPhase", + "nextPhase", + "reason", + "supersededTransitionId" +]); +var MIGRATION_RUN_RETREAT_KEYS = /* @__PURE__ */ new Set(["previousRun", "nextRun", "evidenceHash", "eventData"]); +function rejectUnknownFields(value, keys, label) { + const unknown = Object.keys(value).find((key) => !keys.has(key)); + if (unknown) throw new Error(`${label} contains unknown field: ${unknown}`); +} +function transitionContent(journal) { + return JSON.stringify(journal, null, 2) + "\n"; +} +function migrationStateDocument(state) { + return state.schema === NATIVE_V2_CHANGE_SCHEMA ? nativeV2ChangeDocument(state) : nativeChangeDocument(state); +} +function upgradeV1StateToV2(state, revision) { + return { + ...state, + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision + }; +} +function upgradeV1TransitionToV2(journal) { + return { + ...journal, + schema: NATIVE_V2_TRANSITION_SCHEMA, + minimum_runtime_version: 2, + revision: 1, + previousState: upgradeV1StateToV2(journal.previousState, 1), + nextState: upgradeV1StateToV2(journal.nextState, 2) + }; +} +function upgradeV2StateToV3(state, options) { + const retreat = options?.retreatEvidencePhase === true && (state.phase === "verify" || state.phase === "archive"); + return { + ...state, + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: state.revision + (retreat && options?.incrementRetreatRevision ? 1 : 0), + phase: retreat ? "build" : state.phase, + verification_result: retreat ? "pending" : state.verification_result, + verification_report: retreat ? null : state.verification_report, + approved_contract_hash: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: retreat ? false : state.archived + }; +} +function upgradeV2TransitionToV3(journal) { + if (journal.nextState.phase === "archive") { + throw new Error("Native v2 Archive transition must be superseded by schema migration"); + } + const specRebase = journal.previousState.phase !== "shape" && journal.nextState.phase === "build" && journal.eventData.verificationResult === null; + const evidenceHash = specRebase ? sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: journal.change, + summary: journal.eventData.summary, + specChanges: journal.nextState.spec_changes + }) + ) : journal.evidenceHash; + return { + ...journal, + schema: NATIVE_TRANSITION_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision: 1, + operation: specRebase ? "spec-rebase" : "advance", + evidenceHash, + previousState: upgradeV2StateToV3(journal.previousState), + nextState: { + ...upgradeV2StateToV3(journal.nextState), + ...specRebase ? { + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + } : {} + }, + eventData: { ...journal.eventData, evidenceHash }, + nextRun: journal.previousRun === null ? { + ...journal.nextRun, + skillVersion: NATIVE_RUNTIME_PACKAGE.definition.metadata.version, + skillHash: NATIVE_RUNTIME_HASH + } : journal.nextRun + }; +} +function sameV1State(left, right) { + return JSON.stringify(left) === JSON.stringify(right); +} +function sameV2State(left, right) { + return JSON.stringify(nativeV2ChangeDocument(left)) === JSON.stringify(nativeV2ChangeDocument(right)); +} +function sameCurrentState(left, right) { + return JSON.stringify(nativeChangeDocument(left)) === JSON.stringify(nativeChangeDocument(right)); +} +function sameRunState(left, right) { + return isDeepStrictEqual3(left, right); +} +function parseTransitionSupersede(value, expectedName, nextState, migrationId) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Schema migration transition supersede plan is invalid"); + } + const record8 = value; + rejectUnknownFields( + record8, + MIGRATION_TRANSITION_SUPERSEDE_KEYS, + "Schema migration transition supersede plan" + ); + if (typeof record8.sourceHash !== "string" || !HASH_PATTERN14.test(record8.sourceHash)) { + throw new Error("Schema migration transition supersede source hash is invalid"); + } + if (typeof record8.transitionId !== "string" || record8.transitionId.length === 0) { + throw new Error("Schema migration superseded transition id is invalid"); + } + if (typeof record8.evidenceHash !== "string" || !HASH_PATTERN14.test(record8.evidenceHash)) { + throw new Error("Schema migration transition supersede evidence hash is invalid"); + } + const previousRun = parseNativeStoredRunStateValue(record8.previousRun); + const nextRun = parseNativeStoredRunStateValue(record8.nextRun); + if (previousRun.runId !== nextRun.runId || nextState.run_id !== nextRun.runId || nextState.phase !== "build" && nextState.phase !== "verify" || nextRun.currentStep !== nextState.phase || nextRun.pending !== null || nextRun.status !== "running" || nextState.verification_result !== "pending" || nextState.verification_report !== null || nextState.implementation_scope !== null || nextState.verification_evidence !== null || nextState.partial_allowance !== null) { + throw new Error("Schema migration transition supersede Run does not match target state"); + } + const retreatAllowed = previousRun.currentStep === "verify" && nextState.phase === "build" || previousRun.currentStep === "archive" && nextState.phase === "build"; + const expectedNextRun = previousRun.currentStep === nextState.phase ? previousRun : retreatAllowed ? { + ...previousRun, + currentStep: nextState.phase, + iteration: previousRun.iteration + 1, + pending: null, + status: "running" + } : null; + if (!expectedNextRun || !sameRunState(expectedNextRun, nextRun)) { + throw new Error("Schema migration transition supersede Run retreat is invalid"); + } + if (!record8.eventData || typeof record8.eventData !== "object" || Array.isArray(record8.eventData)) { + throw new Error("Schema migration transition supersede event is invalid"); + } + const eventData = record8.eventData; + const eventKeys = Object.keys(eventData); + if (eventKeys.length !== MIGRATION_SUPERSEDE_EVENT_KEYS.size || eventKeys.some((key) => !MIGRATION_SUPERSEDE_EVENT_KEYS.has(key)) || eventData.fromSchema !== NATIVE_V2_CHANGE_SCHEMA || eventData.toSchema !== NATIVE_CHANGE_SCHEMA || !["build", "verify", "archive"].includes(eventData.previousPhase) || eventData.nextPhase !== nextState.phase || eventData.reason !== (nextState.phase === "build" ? "implementation-scope-required" : "verification-evidence-required") || eventData.supersededTransitionId !== record8.transitionId) { + throw new Error("Schema migration transition supersede event semantics are invalid"); + } + if (nextState.name !== expectedName) { + throw new Error("Schema migration transition supersede change mismatch"); + } + const expectedEvidenceHash = sha256Text( + JSON.stringify({ + operation: "supersede-v2-evidence-transition", + change: expectedName, + transitionId: record8.transitionId, + migrationId, + previousPhase: eventData.previousPhase, + nextPhase: eventData.nextPhase, + reason: eventData.reason, + previousIteration: previousRun.iteration, + nextRevision: nextState.revision + }) + ); + if (record8.evidenceHash !== expectedEvidenceHash) { + throw new Error("Schema migration transition supersede evidence hash mismatch"); + } + return { + sourceHash: record8.sourceHash, + transitionId: record8.transitionId, + previousRun, + nextRun, + evidenceHash: record8.evidenceHash, + eventData + }; +} +function nativeSchemaMigrationJournalFile(paths, name) { + return path38.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); +} +function parseMigrationJournal(value, expectedName) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native schema migration journal must be an object"); + } + rejectUnknownFields( + value, + MIGRATION_JOURNAL_KEYS, + "Native schema migration journal" + ); + const journal = value; + if (journal.schema !== "comet.native.schema-migration.v1") { + throw new Error("Unsupported Native schema migration journal"); + } + if (journal.change !== expectedName) throw new Error("Schema migration change mismatch"); + const v1ToV2 = journal.fromSchema === NATIVE_LEGACY_CHANGE_SCHEMA && journal.toSchema === NATIVE_V2_CHANGE_SCHEMA; + const v2ToV3 = journal.fromSchema === NATIVE_V2_CHANGE_SCHEMA && journal.toSchema === NATIVE_CHANGE_SCHEMA; + if (!v1ToV2 && !v2ToV3) throw new Error("Schema migration route is unsupported"); + if (typeof journal.id !== "string" || journal.id.length === 0) { + throw new Error("Schema migration id is invalid"); + } + if (typeof journal.sourceHash !== "string" || !HASH_PATTERN14.test(journal.sourceHash) || typeof journal.targetHash !== "string" || !HASH_PATTERN14.test(journal.targetHash)) { + throw new Error("Schema migration hash is invalid"); + } + if (typeof journal.createdAt !== "string" || Number.isNaN(Date.parse(journal.createdAt))) { + throw new Error("Schema migration timestamp is invalid"); + } + const nextState = v1ToV2 ? parseV2NativeChangeValue(journal.nextState) : parseNativeChangeValue(journal.nextState); + if (nextState.name !== expectedName) { + throw new Error("Schema migration target state change mismatch"); + } + if (sha256Text((0, import_yaml4.stringify)(migrationStateDocument(nextState))) !== journal.targetHash) { + throw new Error("Schema migration state target hash does not match its document"); + } + let transition; + if (journal.transition !== void 0) { + if (!journal.transition || typeof journal.transition !== "object") { + throw new Error("Schema migration transition target is invalid"); + } + rejectUnknownFields( + journal.transition, + MIGRATION_TRANSITION_KEYS, + "Schema migration transition target" + ); + const transitionValue = journal.transition; + if (typeof transitionValue.sourceHash !== "string" || !HASH_PATTERN14.test(transitionValue.sourceHash) || typeof transitionValue.targetHash !== "string" || !HASH_PATTERN14.test(transitionValue.targetHash)) { + throw new Error("Schema migration transition hash is invalid"); + } + const parsedNextJournal = v1ToV2 ? parseV2NativeTransitionJournalValue(transitionValue.nextJournal, expectedName) : parseNativeTransitionJournalValue(transitionValue.nextJournal, expectedName); + if (sha256Text(JSON.stringify(transitionValue.nextJournal, null, 2) + "\n") !== transitionValue.targetHash) { + throw new Error("Schema migration transition target hash does not match its journal"); + } + const matches = v1ToV2 && nextState.schema === NATIVE_V2_CHANGE_SCHEMA ? sameV2State(nextState, parsedNextJournal.previousState) || sameV2State(nextState, parsedNextJournal.nextState) : nextState.schema === NATIVE_CHANGE_SCHEMA && (sameCurrentState( + nextState, + parsedNextJournal.previousState + ) || sameCurrentState(nextState, parsedNextJournal.nextState)); + if (!matches) throw new Error("Schema migration state/transition target mismatch"); + transition = { + sourceHash: transitionValue.sourceHash, + targetHash: transitionValue.targetHash, + nextJournal: transitionValue.nextJournal + }; + } + if (v1ToV2 && (!transition && nextState.revision !== 1 || transition && nextState.revision !== 1 && nextState.revision !== 2)) { + throw new Error("Schema migration v1 target revision is invalid"); + } + let transitionSupersede; + if (journal.transitionSupersede !== void 0) { + if (!v2ToV3 || transition) { + throw new Error("Schema migration transition supersede plan is not valid for this route"); + } + if (nextState.schema !== NATIVE_CHANGE_SCHEMA) { + throw new Error("Schema migration transition supersede target schema is invalid"); + } + transitionSupersede = parseTransitionSupersede( + journal.transitionSupersede, + expectedName, + nextState, + journal.id + ); + } + let runRetreat; + if (journal.runRetreat !== void 0) { + if (!v2ToV3 || transition || transitionSupersede) { + throw new Error("Schema migration Run retreat is not valid for this route"); + } + if (!journal.runRetreat || typeof journal.runRetreat !== "object") { + throw new Error("Schema migration Run retreat is invalid"); + } + rejectUnknownFields( + journal.runRetreat, + MIGRATION_RUN_RETREAT_KEYS, + "Schema migration Run retreat" + ); + const retreat = journal.runRetreat; + let previousRun; + let nextRun; + try { + previousRun = parseNativeStoredRunStateValue(retreat.previousRun); + nextRun = parseNativeStoredRunStateValue(retreat.nextRun); + } catch (error) { + throw new Error("Schema migration Run retreat is invalid", { cause: error }); + } + if (typeof retreat.evidenceHash !== "string" || !HASH_PATTERN14.test(retreat.evidenceHash) || !retreat.eventData || typeof retreat.eventData !== "object" || Array.isArray(retreat.eventData)) { + throw new Error("Schema migration Run retreat is invalid"); + } + if (nextState.schema !== NATIVE_CHANGE_SCHEMA || nextState.phase !== "build" || nextState.run_id !== nextRun.runId || previousRun.runId !== nextRun.runId || previousRun.currentStep !== "verify" && previousRun.currentStep !== "archive" || nextRun.currentStep !== "build" || nextRun.iteration !== previousRun.iteration + 1) { + throw new Error("Schema migration Run retreat does not match target state"); + } + runRetreat = { + previousRun, + nextRun, + evidenceHash: retreat.evidenceHash, + eventData: retreat.eventData + }; + } + if (transition && runRetreat || transitionSupersede && runRetreat) { + throw new Error("Schema migration has conflicting recovery plans"); + } + return { + schema: "comet.native.schema-migration.v1", + id: journal.id, + change: expectedName, + fromSchema: journal.fromSchema, + toSchema: journal.toSchema, + sourceHash: journal.sourceHash, + targetHash: journal.targetHash, + createdAt: journal.createdAt, + nextState, + ...transition ? { transition } : {}, + ...transitionSupersede ? { transitionSupersede } : {}, + ...runRetreat ? { runRetreat } : {} + }; +} +async function inspectPendingNativeSchemaMigration(paths, name) { + const file = nativeSchemaMigrationJournalFile(paths, name); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + const source = await readNativeProtectedTextFile({ + root: paths.nativeRoot, + file, + maxBytes: 512 * 1024, + label: "Native schema migration journal" + }); + return parseMigrationJournal(JSON.parse(source.text), name); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function ensureMigrationBaseline(paths, name, createdAt) { + const stored = await readNativeBaselineManifest(paths, name); + const baseline = stored ? await filterNativeContentSnapshotToProjectScope(paths, stored) : await createNativeContentSnapshot(paths, { + now: new Date(createdAt), + origin: "legacy-migration" + }); + if (!baseline.complete) { + const health = inspectNativeContentSnapshotHealth(baseline); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + throw new NativeBaselineIncompleteError( + name, + baseline.omittedCount, + omittedByReason, + health.samplePaths, + health.sampleTruncated + ); + } + if (stored === null) await writeNativeBaselineManifest(paths, name, baseline); +} +async function optionalFileHash(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +function expectedSupersedeEvent(journal, sequence) { + const supersede = journal.transitionSupersede; + return { + sequence, + timestamp: journal.createdAt, + type: "state_migrated", + runId: supersede.nextRun.runId, + data: { + ...supersede.eventData, + migrationId: journal.id, + evidenceHash: supersede.evidenceHash + } + }; +} +async function inspectSupersedeEvent(paths, journal) { + const supersede = journal.transitionSupersede; + const changeDir = nativeChangeDir(paths, journal.change); + const trajectory = await readNativeTrajectory(changeDir, supersede.nextRun.trajectoryRef); + const matches = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.migrationId === journal.id); + if (matches.length > 1) { + throw new Error(`Native schema migration event ${journal.id} is duplicated`); + } + const match = matches[0]; + if (match && !isDeepStrictEqual3(match.event, expectedSupersedeEvent(journal, match.index + 1))) { + throw new Error(`Native schema migration event ${journal.id} changed`); + } + return { trajectoryLength: trajectory.length, sequence: match?.event.sequence ?? null }; +} +async function assertSupersedeSourceBeforeMutation(paths, journal, stateAtTarget) { + if (!journal.transitionSupersede) return; + const transitionFile = nativeTransitionJournalFile(paths, journal.change); + const actualHash = await optionalFileHash(transitionFile); + if (actualHash === journal.transitionSupersede.sourceHash) return; + if (actualHash !== null) { + throw new Error( + `Native superseded transition source changed for ${journal.change}: expected ${journal.transitionSupersede.sourceHash}, actual ${actualHash}` + ); + } + const run = await readNativeRunState(nativeChangeDir(paths, journal.change)); + const event = await inspectSupersedeEvent(paths, journal); + const checkpoint = await readNativeCheckpoint( + nativeChangeDir(paths, journal.change), + journal.transitionSupersede.nextRun.checkpointRef + ); + if (!stateAtTarget || !run || !sameRunState(run, journal.transitionSupersede.nextRun) || event.sequence === null || !checkpoint || checkpoint.runId !== journal.transitionSupersede.nextRun.runId || checkpoint.stateVersion !== journal.transitionSupersede.nextRun.iteration || checkpoint.trajectoryOffset !== event.sequence || checkpoint.contextHash !== null || checkpoint.artifactsHash !== sha256Text(journal.transitionSupersede.evidenceHash) || checkpoint.createdAt !== journal.createdAt) { + throw new Error( + `Native superseded transition disappeared before migration ${journal.id} was durable` + ); + } +} +async function continueTransitionSupersede(paths, journal, hooks) { + const supersede = journal.transitionSupersede; + if (!supersede) return; + const changeDir = nativeChangeDir(paths, journal.change); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) { + throw new Error(`Native schema migration Run state disappeared for ${journal.change}`); + } + if (!sameRunState(currentRun, supersede.nextRun)) { + if (!sameRunState(currentRun, supersede.previousRun)) { + throw new Error(`Native schema migration Run source changed for ${journal.change}`); + } + await writeNativeRunState(changeDir, supersede.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + if (!sameRunState(await readNativeRunState(changeDir), supersede.nextRun)) { + throw new Error(`Native schema migration Run write diverged for ${journal.change}`); + } + let event = await inspectSupersedeEvent(paths, journal); + if (event.sequence === null) { + const appended = await appendNativeTrajectoryEvent({ + changeDir, + run: supersede.nextRun, + type: "state_migrated", + data: { + ...supersede.eventData, + migrationId: journal.id, + evidenceHash: supersede.evidenceHash + }, + now: new Date(journal.createdAt) + }); + event = { trajectoryLength: appended.sequence, sequence: appended.sequence }; + } + await hooks?.afterTrajectoryWritten?.(journal); + await writeNativeCheckpoint2({ + changeDir, + run: supersede.nextRun, + trajectoryOffset: event.sequence, + evidenceHash: supersede.evidenceHash, + now: new Date(journal.createdAt) + }); + await hooks?.afterCheckpointWritten?.(journal); + const transitionFile = nativeTransitionJournalFile(paths, journal.change); + const transitionHash = await optionalFileHash(transitionFile); + if (transitionHash !== null) { + if (transitionHash !== supersede.sourceHash) { + throw new Error(`Native superseded transition changed before removal for ${journal.change}`); + } + await fs25.rm(transitionFile); + await hooks?.afterTransitionSuperseded?.(journal); + } +} +async function continueRunRetreat(paths, journal, hooks) { + if (!journal.runRetreat) return; + const changeDir = nativeChangeDir(paths, journal.change); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) { + throw new Error(`Native schema migration Run state disappeared for ${journal.change}`); + } + if (!sameRunState(currentRun, journal.runRetreat.nextRun)) { + if (!sameRunState(currentRun, journal.runRetreat.previousRun)) { + throw new Error(`Native schema migration Run source changed for ${journal.change}`); + } + await writeNativeRunState(changeDir, journal.runRetreat.nextRun); + await hooks?.afterRunStateWritten?.(journal); + } + let trajectory = await readNativeTrajectory(changeDir, journal.runRetreat.nextRun.trajectoryRef); + let event = trajectory.find( + (item) => item.type === "state_migrated" && item.data.migrationId === journal.id + ); + if (!event) { + event = await appendNativeTrajectoryEvent({ + changeDir, + run: journal.runRetreat.nextRun, + type: "state_migrated", + data: { + ...journal.runRetreat.eventData, + migrationId: journal.id, + evidenceHash: journal.runRetreat.evidenceHash + }, + now: new Date(journal.createdAt) + }); + trajectory = [...trajectory, event]; + } + await hooks?.afterTrajectoryWritten?.(journal); + await writeNativeCheckpoint2({ + changeDir, + run: journal.runRetreat.nextRun, + trajectoryOffset: trajectory.length, + evidenceHash: journal.runRetreat.evidenceHash, + now: new Date(journal.createdAt) + }); + await hooks?.afterCheckpointWritten?.(journal); +} +async function continueNativeSchemaMigrationLocked(paths, name, hooks) { + const journal = await inspectPendingNativeSchemaMigration(paths, name); + if (!journal) return null; + if (await hasPendingNativeCheckpointRecovery(paths, name)) { + throw new Error( + `Native change ${name} has a pending progress checkpoint; recover it with its v2 runtime before schema migration` + ); + } + const changeFile = path38.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + const actualHash = await sha256File(changeFile); + await assertSupersedeSourceBeforeMutation(paths, journal, actualHash === journal.targetHash); + await ensureMigrationBaseline(paths, name, journal.createdAt); + if (actualHash !== journal.targetHash) { + if (actualHash !== journal.sourceHash) { + throw new Error( + `Native schema migration source changed for ${name}: expected ${journal.sourceHash}, actual ${actualHash}` + ); + } + await atomicWriteText(changeFile, (0, import_yaml4.stringify)(migrationStateDocument(journal.nextState))); + await hooks?.afterStateWritten?.(journal); + } + if (journal.transition) { + const transitionFile = nativeTransitionJournalFile(paths, name); + const actualTransitionHash = await sha256File(transitionFile); + if (actualTransitionHash !== journal.transition.targetHash) { + if (actualTransitionHash !== journal.transition.sourceHash) { + throw new Error( + `Native transition migration source changed for ${name}: expected ${journal.transition.sourceHash}, actual ${actualTransitionHash}` + ); + } + await atomicWriteJson(transitionFile, journal.transition.nextJournal); + await hooks?.afterTransitionWritten?.(journal); + } + } + await continueTransitionSupersede(paths, journal, hooks); + await continueRunRetreat(paths, journal, hooks); + await fs25.rm(nativeSchemaMigrationJournalFile(paths, name), { force: true }); + return journal.nextState; +} +async function stableEvidenceRetreat(options) { + const changeDir = nativeChangeDir(options.paths, options.state.name); + const run = await readNativeRunState(changeDir); + if (!run || run.runId !== options.state.run_id || run.currentStep !== options.state.phase || run.currentStep !== "verify" && run.currentStep !== "archive" || run.pending !== null) { + throw new Error( + `Native v2 ${options.state.phase} change ${options.state.name} has no consistent Run state for safe Build retreat` + ); + } + const nextRun = { + ...run, + currentStep: "build", + iteration: run.iteration + 1, + pending: null, + status: "running" + }; + const evidenceHash = sha256Text( + `schema-v3-evidence-retreat:${options.state.name}:${options.state.revision}:${options.migrationId}` + ); + return { + previousRun: run, + nextRun, + evidenceHash, + eventData: { + fromSchema: NATIVE_V2_CHANGE_SCHEMA, + toSchema: NATIVE_CHANGE_SCHEMA, + previousPhase: options.state.phase, + nextPhase: "build", + reason: "implementation-scope-required" + } + }; +} +async function pendingEvidenceTransitionSupersede(options) { + const { journal, state } = options; + const requiresV3Evidence = journal.previousState.phase === "build" && journal.nextState.phase === "verify" || journal.previousState.phase === "verify" && (journal.nextState.phase === "build" || journal.nextState.phase === "archive"); + if (!requiresV3Evidence) { + throw new Error("Only a pending v2 evidence-bearing transition can be superseded"); + } + const stateIsPrevious = sameV2State(state, journal.previousState); + const stateIsNext = sameV2State(state, journal.nextState); + if (!stateIsPrevious && !stateIsNext) { + throw new Error(`Native change ${state.name} does not match its v2 evidence transition`); + } + const changeDir = nativeChangeDir(options.paths, state.name); + const currentRun = await readNativeRunState(changeDir); + if (!currentRun) throw new Error(`Native v2 transition Run is missing for ${state.name}`); + const runIsPrevious = journal.previousRun !== null && sameRunState(currentRun, journal.previousRun); + const runIsNext = sameRunState(currentRun, journal.nextRun); + if (!runIsPrevious && !runIsNext || stateIsNext && !runIsNext) { + throw new Error(`Native v2 transition Run does not match its durable state for ${state.name}`); + } + const trajectory = await readNativeTrajectory(changeDir, journal.nextRun.trajectoryRef); + const collisions = trajectory.map((event, index) => ({ event, index })).filter(({ event }) => event.data.transitionId === journal.id); + if (collisions.length > 1) { + throw new Error(`Native v2 evidence transition ${journal.id} has duplicate trajectory events`); + } + const durable = collisions[0]; + if (durable) { + const expected = { + sequence: durable.index + 1, + timestamp: journal.createdAt, + type: "state_transitioned", + runId: journal.nextRun.runId, + data: { ...journal.eventData, transitionId: journal.id } + }; + if (!isDeepStrictEqual3(durable.event, expected) || !stateIsNext || !runIsNext) { + throw new Error( + `Native v2 evidence transition ${journal.id} trajectory event does not match its journal` + ); + } + } + const nextState = { + ...upgradeV2StateToV3(state), + revision: state.revision + (state.phase === "build" ? 0 : 1), + phase: "build", + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null, + archived: false + }; + const previousRun = currentRun; + const nextRun = currentRun.currentStep !== "build" ? { + ...currentRun, + currentStep: "build", + iteration: currentRun.iteration + 1, + pending: null, + status: "running" + } : currentRun; + const evidenceHash = sha256Text( + JSON.stringify({ + operation: "supersede-v2-evidence-transition", + change: state.name, + transitionId: journal.id, + migrationId: options.migrationId, + previousPhase: state.phase, + nextPhase: "build", + reason: "implementation-scope-required", + previousIteration: currentRun.iteration, + nextRevision: nextState.revision + }) + ); + return { + nextState, + plan: { + sourceHash: await sha256File(nativeTransitionJournalFile(options.paths, state.name)), + transitionId: journal.id, + previousRun, + nextRun, + evidenceHash, + eventData: { + fromSchema: NATIVE_V2_CHANGE_SCHEMA, + toSchema: NATIVE_CHANGE_SCHEMA, + previousPhase: state.phase, + nextPhase: "build", + reason: "implementation-scope-required", + supersededTransitionId: journal.id + } + } + }; +} +async function prepareNextMigration(options) { + let nextState; + let transition; + let transitionSupersede; + let runRetreat; + if (options.state.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + nextState = upgradeV1StateToV2(options.state, 1); + if (options.pendingTransition) { + if (options.pendingTransition.journal.schema !== NATIVE_LEGACY_TRANSITION_SCHEMA) { + throw new Error("Native v1 change has a transition from another schema generation"); + } + const nextJournal = upgradeV1TransitionToV2(options.pendingTransition.journal); + if (sameV1State(options.state, options.pendingTransition.journal.previousState)) { + nextState = nextJournal.previousState; + } else if (sameV1State(options.state, options.pendingTransition.journal.nextState)) { + nextState = nextJournal.nextState; + } else { + throw new Error( + `Native change ${options.name} does not match either state in its v1 transition journal` + ); + } + const transitionFile = nativeTransitionJournalFile(options.paths, options.name); + transition = { + sourceHash: await sha256File(transitionFile), + targetHash: sha256Text(transitionContent(nextJournal)), + nextJournal + }; + } + } else { + if (options.pendingTransition) { + if (options.pendingTransition.journal.schema !== NATIVE_V2_TRANSITION_SCHEMA) { + throw new Error("Native v2 change has a transition from another schema generation"); + } + const v2Journal = options.pendingTransition.journal; + const requiresV3Evidence = v2Journal.previousState.phase === "build" && v2Journal.nextState.phase === "verify" || v2Journal.previousState.phase === "verify" && (v2Journal.nextState.phase === "build" || v2Journal.nextState.phase === "archive"); + if (requiresV3Evidence) { + const supersede = await pendingEvidenceTransitionSupersede({ + paths: options.paths, + state: options.state, + journal: v2Journal, + migrationId: options.id + }); + nextState = supersede.nextState; + transitionSupersede = supersede.plan; + } else { + const nextJournal = upgradeV2TransitionToV3(v2Journal); + if (sameV2State(options.state, options.pendingTransition.journal.previousState)) { + nextState = nextJournal.previousState; + } else if (sameV2State(options.state, options.pendingTransition.journal.nextState)) { + nextState = nextJournal.nextState; + } else { + throw new Error( + `Native change ${options.name} does not match either state in its v2 transition journal` + ); + } + const transitionFile = nativeTransitionJournalFile(options.paths, options.name); + transition = { + sourceHash: await sha256File(transitionFile), + targetHash: sha256Text(transitionContent(nextJournal)), + nextJournal + }; + } + } else { + nextState = upgradeV2StateToV3(options.state, { + retreatEvidencePhase: true, + incrementRetreatRevision: true + }); + if (options.state.phase === "verify" || options.state.phase === "archive") { + runRetreat = await stableEvidenceRetreat({ + paths: options.paths, + state: options.state, + migrationId: options.id + }); + } + } + } + const changeFile = path38.join( + nativeChangeDir(options.paths, options.name), + NATIVE_CHANGE_STATE_FILE + ); + const targetContent = (0, import_yaml4.stringify)(migrationStateDocument(nextState)); + return { + schema: "comet.native.schema-migration.v1", + id: options.id, + change: options.name, + fromSchema: options.state.schema, + toSchema: options.state.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? NATIVE_V2_CHANGE_SCHEMA : NATIVE_CHANGE_SCHEMA, + sourceHash: await sha256File(changeFile), + targetHash: sha256Text(targetContent), + createdAt: options.now.toISOString(), + nextState, + ...transition ? { transition } : {}, + ...transitionSupersede ? { transitionSupersede } : {}, + ...runRetreat ? { runRetreat } : {} + }; +} +async function migrateNativeChange(options) { + return withNativeMutationLock( + options.paths, + `migrate schema for ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `migrate schema for ${options.name}`, + async () => { + for (let step = 0; step < 3; step += 1) { + const continued = await continueNativeSchemaMigrationLocked( + options.paths, + options.name, + options.hooks + ); + if (continued?.schema === NATIVE_CHANGE_SCHEMA) return continued; + const inspection = await inspectNativeChange(options.paths, options.name); + if (inspection.status === "current" && inspection.state) { + return inspection.state; + } + if (inspection.status !== "migration-required" || !inspection.state) { + throw new Error( + inspection.message ?? `Native change ${options.name} cannot be migrated` + ); + } + if (await hasPendingNativeCheckpointRecovery(options.paths, options.name)) { + throw new Error( + `Native change ${options.name} has a pending progress checkpoint; recover it with its v2 runtime before schema migration` + ); + } + const state = inspection.state; + if (state.schema !== NATIVE_LEGACY_CHANGE_SCHEMA && state.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Native change ${options.name} has no supported migration route`); + } + const pendingTransition = await inspectPendingNativeTransitionSchema( + options.paths, + options.name + ); + if (pendingTransition?.status === "current") { + throw new Error( + `Native change ${options.name} has a current-schema pending transition; recover it before schema migration` + ); + } + const journal = await prepareNextMigration({ + paths: options.paths, + name: options.name, + state, + pendingTransition, + now: options.now ?? /* @__PURE__ */ new Date(), + id: options.id?.() ?? randomUUID7() + }); + const journalFile = nativeSchemaMigrationJournalFile(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, journalFile); + await atomicWriteJson(journalFile, journal); + await options.hooks?.afterPrepared?.(journal); + } + throw new Error( + `Native change ${options.name} exceeded the supported schema migration path` + ); + } + ) + ); +} + +// domains/comet-native/native-evidence-retention.ts +var NATIVE_EVIDENCE_RETENTION_POLICY = Object.freeze({ + minimumAgeMs: 30 * 24 * 60 * 60 * 1e3, + keepLatestUnreferencedPerKind: 32, + maxDocuments: 4096, + maxScannedBytes: 256 * 1024 * 1024, + maxReportedRefs: 8, + maxReportMessageBytes: 4096 +}); +var MAX_NATIVE_CHECK_RECEIPT_BYTES2 = 512 * 1024; +var HASH_FILE_PATTERN = /^([a-f0-9]{64})\.json$/u; +var CLEANUP_QUARANTINE_PATTERN = /^\.([a-f0-9]{64}\.json)\.([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})\.gc$/u; +var MANAGED_REF_PATTERN = /^runtime\/evidence\/(snapshots|scopes|allowances|verifications|reports|check-receipts)\/[a-f0-9]{64}\.json$/u; +var MANAGED_KINDS = [ + "snapshots", + "scopes", + "allowances", + "verifications", + "reports", + "check-receipts" +]; +function sameObjectIdentity(left, right) { + return sameFileObject( + { ...left, birthtime: left.birthtimeMs }, + { + ...right, + birthtime: right.birthtimeMs + } + ); +} +function sameIdentity(left, right) { + return sameObjectIdentity(left, right) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; +} +function sameContentVersionAfterLinkOrRename(left, right) { + return sameObjectIdentity(left, right) && left.birthtimeMs === right.birthtimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; +} +function fileIdentity2(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function compareText8(left, right) { + return left < right ? -1 : left > right ? 1 : 0; +} +function portableProjectRef(paths, target) { + const relative = path39.relative(paths.projectRoot, target); + if (relative.length === 0 || path39.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path39.sep}`)) { + throw new Error("Native evidence retention finding path is outside the project"); + } + return relative.split(path39.sep).join("/"); +} +function evidenceRootPath(paths, name) { + return path39.join(nativeChangeDir(paths, name), "runtime", "evidence"); +} +function evidenceRootRef(paths, name) { + return portableProjectRef(paths, evidenceRootPath(paths, name)); +} +async function captureDirectoryChain6(managedRoot, directory) { + const root = path39.resolve(managedRoot); + const target = path39.resolve(directory); + if (!isInsidePath(root, target)) { + throw new Error("Native evidence retention path escapes its managed change"); + } + const segments = path39.relative(root, target).split(path39.sep).filter(Boolean); + const chain = []; + let cursor = root; + for (const segment of ["", ...segments]) { + if (segment) cursor = path39.join(cursor, segment); + const stat = await fs26.lstat(cursor); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native evidence retention parent must be a real directory: ${cursor}`); + } + const realPath = await fs26.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new Error(`Native evidence retention parent resolves outside its change: ${cursor}`); + } + chain.push({ path: cursor, realPath, ...fileIdentity2(stat) }); + } + return chain; +} +async function verifyDirectoryChain7(chain) { + for (const identity of chain) { + const stat = await fs26.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameObjectIdentity(identity, stat) || await fs26.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native evidence retention parent changed: ${identity.path}`); + } + } +} +function refForKind(kind, hash6) { + return kind === "check-receipts" ? `runtime/evidence/check-receipts/${hash6}.json` : nativeEvidenceRef(kind, hash6); +} +function managedDependency(value) { + return value !== null && MANAGED_REF_PATTERN.test(value) ? [value] : []; +} +function parseDocument4(kind, hash6, value, expectedChange) { + if (kind === "snapshots") { + return { + canonical: parseNativeSnapshotProjection(value, hash6), + dependencies: [] + }; + } + if (kind === "scopes") { + const scope = parseNativeImplementationScope(value); + if (scope.scopeHash !== hash6) throw new Error("Native scope filename does not match its hash"); + return { + canonical: scope, + dependencies: [scope.baselineProjectionRef, scope.currentProjectionRef] + }; + } + if (kind === "allowances") { + const allowance = parseNativePartialAllowance(value); + if (allowance.change !== expectedChange || allowance.allowanceHash !== hash6) { + throw new Error("Native allowance filename/change does not match its content"); + } + return { + canonical: allowance, + dependencies: [nativeEvidenceRef("scopes", allowance.scopeHash)] + }; + } + if (kind === "verifications") { + const evidence = parseNativeVerificationEvidenceEnvelope(value); + if (evidence.change !== expectedChange || evidence.envelopeHash !== hash6) { + throw new Error("Native verification filename/change does not match its content"); + } + const traceDependencies = evidence.acceptanceTrace.entries.flatMap( + (entry2) => entry2.evidenceRefs.flatMap(managedDependency) + ); + return { + canonical: evidence, + dependencies: [ + evidence.implementationScopeRef, + nativeReportEvidenceRef(evidence.reportHash), + ...managedDependency(evidence.partialAllowanceRef), + ...managedDependency(evidence.receiptRef), + ...traceDependencies + ] + }; + } + if (kind === "reports") { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native report evidence must be an object"); + } + const report = value; + if (Object.keys(report).sort().join(",") !== "content,reportHash,schema" || report.schema !== "comet.native.verification-report.v1" || report.reportHash !== hash6 || typeof report.content !== "string" || createHash13("sha256").update(Buffer.from(report.content, "utf8")).digest("hex") !== hash6) { + throw new Error("Native report evidence filename/hash does not match its content"); + } + return { canonical: report, dependencies: [] }; + } + const receipt = parseNativeCheckReceipt(value); + if (receipt.change !== expectedChange || receipt.receiptHash !== hash6) { + throw new Error("Native check receipt filename/change does not match its content"); + } + return { canonical: receipt, dependencies: [] }; +} +async function readCanonicalDocument(options) { + const before = await fs26.lstat(options.file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native evidence entry is not a regular file: ${options.ref}`); + } + const maximumBytes = options.kind === "check-receipts" ? MAX_NATIVE_CHECK_RECEIPT_BYTES2 : MAX_NATIVE_EVIDENCE_DOCUMENT_BYTES; + if (before.size > maximumBytes) { + throw new Error(`Native evidence entry exceeds its byte budget: ${options.ref}`); + } + const beforeRealPath = await fs26.realpath(options.file); + if (!isInsidePath(options.directoryChain[0].realPath, beforeRealPath)) { + throw new Error(`Native evidence entry resolves outside its change: ${options.ref}`); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants5.O_RDONLY | fsConstants5.O_NOFOLLOW | fsConstants5.O_NONBLOCK; + const handle = await fs26.open(options.file, openFlags).catch((error) => { + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new Error(`Native evidence entry became unsafe while opening: ${options.ref}`); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs26.lstat(options.file), + fs26.realpath(options.file) + ]); + await verifyDirectoryChain7(options.directoryChain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameIdentity(fileIdentity2(before), opened) || !sameIdentity(fileIdentity2(opened), pathAfterOpen)) { + throw new Error(`Native evidence entry changed while opening: ${options.ref}`); + } + const chunks = []; + const buffer = Buffer.allocUnsafe(64 * 1024); + let total = 0; + while (true) { + const remaining = maximumBytes + 1 - total; + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > maximumBytes) { + throw new Error(`Native evidence entry exceeds its byte budget: ${options.ref}`); + } + chunks.push(Buffer.from(buffer.subarray(0, read.bytesRead))); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs26.lstat(options.file), + fs26.realpath(options.file) + ]); + await verifyDirectoryChain7(options.directoryChain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameIdentity(fileIdentity2(opened), afterHandle) || !sameIdentity(fileIdentity2(opened), afterPath)) { + throw new Error(`Native evidence entry changed while reading: ${options.ref}`); + } + const text = Buffer.concat(chunks, total).toString("utf8"); + let json; + try { + json = JSON.parse(text); + } catch (error) { + throw new Error(`Native evidence entry is not valid JSON: ${options.ref}`, { + cause: error + }); + } + const parsed = parseDocument4(options.kind, options.hash, json, options.name); + if (text !== JSON.stringify(parsed.canonical, null, 2) + "\n") { + throw new Error(`Native evidence entry is not canonically serialized: ${options.ref}`); + } + return { + ref: options.ref, + kind: options.kind, + change: options.name, + changeRoot: options.changeRoot, + file: options.file, + size: total, + mtimeMs: afterHandle.mtimeMs, + identity: fileIdentity2(afterHandle), + directoryChain: options.directoryChain, + dependencies: [...new Set(parsed.dependencies)].sort(compareText8) + }; + } finally { + await handle.close(); + } +} +async function pathIsMissing(file) { + try { + await fs26.lstat(file); + return false; + } catch (error) { + if (error.code === "ENOENT") return true; + throw error; + } +} +async function inspectCleanupQuarantines(options) { + const changeRoot = nativeChangeDir(options.paths, options.name); + const evidenceRoot = evidenceRootPath(options.paths, options.name); + await resolveContainedNativePath(options.paths.nativeRoot, evidenceRoot); + let rootEntries; + try { + const rootChain = await captureDirectoryChain6(changeRoot, evidenceRoot); + rootEntries = await fs26.readdir(evidenceRoot, { withFileTypes: true }); + await verifyDirectoryChain7(rootChain); + } catch (error) { + if (error.code === "ENOENT") { + return { findings: [], pending: false }; + } + throw error; + } + const recoveries = []; + for (const kind of MANAGED_KINDS) { + if (!rootEntries.some((entry2) => entry2.name === kind)) continue; + const directory = path39.join(evidenceRoot, kind); + const directoryChain = await captureDirectoryChain6(changeRoot, directory); + const entries = (await fs26.readdir(directory, { withFileTypes: true })).sort( + (left, right) => compareText8(left.name, right.name) + ); + const byOriginal = /* @__PURE__ */ new Map(); + for (const entry2 of entries) { + const match = CLEANUP_QUARANTINE_PATTERN.exec(entry2.name); + if (!match) continue; + const grouped = byOriginal.get(match[1]) ?? []; + grouped.push(entry2); + byOriginal.set(match[1], grouped); + } + for (const [originalName, entriesForOriginal] of [...byOriginal.entries()].sort( + ([left], [right]) => compareText8(left, right) + )) { + if (entriesForOriginal.length !== 1) { + throw new Error( + `Native evidence cleanup has multiple quarantines for ${kind}/${originalName}` + ); + } + const entry2 = entriesForOriginal[0]; + if (!entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error( + `Native evidence cleanup quarantine is not a regular file: ${kind}/${entry2.name}` + ); + } + const original = path39.join(directory, originalName); + const hash6 = HASH_FILE_PATTERN.exec(originalName)?.[1]; + if (!hash6) + throw new Error(`Native evidence cleanup quarantine name is invalid: ${entry2.name}`); + const ref = refForKind(kind, hash6); + const quarantine = path39.join(directory, entry2.name); + const document = await readCanonicalDocument({ + changeRoot, + file: quarantine, + ref, + kind, + hash: hash6, + name: options.name, + directoryChain + }); + const originalMissing = await pathIsMissing(original); + const originalDocument = originalMissing ? null : await readCanonicalDocument({ + changeRoot, + file: original, + ref, + kind, + hash: hash6, + name: options.name, + directoryChain + }); + recoveries.push({ + ref, + original, + quarantine, + document, + action: originalMissing ? "restore-original" : "finish-linked-recovery", + originalDocument + }); + } + } + if (recoveries.length === 0) return { findings: [], pending: false }; + const refs = recoveries.map((recovery) => recovery.ref).sort(compareText8); + if (!options.repair) { + return { + pending: true, + findings: [ + { + severity: "warning", + code: "evidence-retention-recovery-required", + message: boundedMessage( + `Native evidence cleanup has ${refs.length} safely recoverable quarantined document(s) for ${options.name}: ${boundedRefs(refs)}` + ), + path: evidenceRootRef(options.paths, options.name) + } + ] + }; + } + for (const recovery of recoveries.sort((left, right) => compareText8(left.ref, right.ref))) { + await verifyDirectoryChain7(recovery.document.directoryChain); + if (recovery.action === "finish-linked-recovery") { + const original = await readCanonicalDocument({ + changeRoot, + file: recovery.original, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + const quarantine = await readCanonicalDocument({ + changeRoot, + file: recovery.quarantine, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + if (!recovery.originalDocument || !sameIdentity(recovery.originalDocument.identity, original.identity) || !sameIdentity(recovery.document.identity, quarantine.identity)) { + throw new Error( + `Native evidence cleanup original or quarantine changed during recovery: ${recovery.ref}` + ); + } + await fs26.rm(recovery.quarantine); + await verifyDirectoryChain7(recovery.document.directoryChain); + continue; + } + if (!await pathIsMissing(recovery.original)) { + throw new Error(`Native evidence cleanup original appeared during recovery: ${recovery.ref}`); + } + const quarantineStat = await fs26.lstat(recovery.quarantine); + if (!quarantineStat.isFile() || quarantineStat.isSymbolicLink() || !sameIdentity(recovery.document.identity, quarantineStat)) { + throw new Error( + `Native evidence cleanup quarantine changed during recovery: ${recovery.ref}` + ); + } + await fs26.link(recovery.quarantine, recovery.original); + await options.hooks.afterRecoveryLink?.({ + ref: recovery.ref, + original: recovery.original, + quarantine: recovery.quarantine + }); + const restored = await readCanonicalDocument({ + changeRoot, + file: recovery.original, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + const linkedQuarantine = await readCanonicalDocument({ + changeRoot, + file: recovery.quarantine, + ref: recovery.ref, + kind: recovery.document.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(recovery.original))[1], + name: options.name, + directoryChain: recovery.document.directoryChain + }); + if (!sameContentVersionAfterLinkOrRename(recovery.document.identity, restored.identity) || !sameIdentity(restored.identity, linkedQuarantine.identity)) { + throw new Error(`Native evidence cleanup recovery identity changed: ${recovery.ref}`); + } + await fs26.rm(recovery.quarantine); + await verifyDirectoryChain7(recovery.document.directoryChain); + } + return { + pending: false, + findings: [ + { + severity: "info", + code: "evidence-retention-cleanup-recovered", + message: boundedMessage( + `Recovered ${refs.length} interrupted Native evidence cleanup document(s) for ${options.name}: ${boundedRefs(refs)}` + ), + path: evidenceRootRef(options.paths, options.name) + } + ] + }; +} +async function scanEvidenceStore(paths, name) { + const changeRoot = nativeChangeDir(paths, name); + const evidenceRoot = path39.join(changeRoot, "runtime", "evidence"); + await resolveContainedNativePath(paths.nativeRoot, evidenceRoot); + let rootEntries; + try { + const rootChain = await captureDirectoryChain6(changeRoot, evidenceRoot); + rootEntries = await fs26.readdir(evidenceRoot, { withFileTypes: true }); + await verifyDirectoryChain7(rootChain); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const unknownRootEntries = rootEntries.filter( + (entry2) => !MANAGED_KINDS.includes(entry2.name) || !entry2.isDirectory() || entry2.isSymbolicLink() + ).map((entry2) => entry2.name).sort(compareText8); + if (unknownRootEntries.length > 0) { + throw new Error( + `Native evidence root has unknown or special entries: ${boundedRefs(unknownRootEntries)}` + ); + } + const documents = []; + let scannedBytes = 0; + for (const kind of MANAGED_KINDS) { + if (!rootEntries.some((entry2) => entry2.name === kind)) continue; + const directory = path39.join(evidenceRoot, kind); + const directoryChain = await captureDirectoryChain6(changeRoot, directory); + const entries = (await fs26.readdir(directory, { withFileTypes: true })).sort( + (left, right) => compareText8(left.name, right.name) + ); + await verifyDirectoryChain7(directoryChain); + for (const entry2 of entries) { + if (documents.length >= NATIVE_EVIDENCE_RETENTION_POLICY.maxDocuments) { + throw new Error( + `Native evidence store exceeds ${NATIVE_EVIDENCE_RETENTION_POLICY.maxDocuments} documents` + ); + } + const match = HASH_FILE_PATTERN.exec(entry2.name); + if (!match || !entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error( + `Native evidence kind ${kind} has an unknown or special entry: ${entry2.name}` + ); + } + const ref = refForKind(kind, match[1]); + const file = path39.join(directory, entry2.name); + await resolveContainedNativePath(paths.nativeRoot, file); + const document = await readCanonicalDocument({ + changeRoot, + file, + ref, + kind, + hash: match[1], + name, + directoryChain + }); + scannedBytes += document.size; + if (scannedBytes > NATIVE_EVIDENCE_RETENTION_POLICY.maxScannedBytes) { + throw new Error( + `Native evidence store exceeds ${NATIVE_EVIDENCE_RETENTION_POLICY.maxScannedBytes} scanned bytes` + ); + } + documents.push(document); + } + await verifyDirectoryChain7(directoryChain); + } + return documents; +} +function closeDependencies(seeds, documents) { + const retained = /* @__PURE__ */ new Set(); + const pending = [...seeds].sort(compareText8).reverse(); + while (pending.length > 0) { + const ref = pending.pop(); + if (retained.has(ref)) continue; + const document = documents.get(ref); + if (!document) throw new Error(`Native evidence dependency is missing: ${ref}`); + retained.add(ref); + for (const dependency of [...document.dependencies].sort(compareText8).reverse()) { + if (!retained.has(dependency)) pending.push(dependency); + } + } + return retained; +} +function dependentsBeforeDependencies(candidates) { + const byRef = new Map(candidates.map((candidate) => [candidate.ref, candidate])); + const dependencyCounts = new Map(candidates.map((candidate) => [candidate.ref, 0])); + for (const candidate of candidates) { + for (const dependency of candidate.dependencies) { + if (byRef.has(dependency)) { + dependencyCounts.set(dependency, (dependencyCounts.get(dependency) ?? 0) + 1); + } + } + } + const ready = [...dependencyCounts].filter(([, count]) => count === 0).map(([ref]) => ref).sort(compareText8); + const ordered = []; + while (ready.length > 0) { + const ref = ready.shift(); + const candidate = byRef.get(ref); + ordered.push(candidate); + for (const dependency of candidate.dependencies) { + const count = dependencyCounts.get(dependency); + if (count === void 0) continue; + const next = count - 1; + dependencyCounts.set(dependency, next); + if (next === 0) { + ready.push(dependency); + ready.sort(compareText8); + } + } + } + if (ordered.length !== candidates.length) { + throw new Error("Native evidence candidate dependency graph contains a cycle"); + } + return ordered; +} +function planRetention(documents, rootRefs, nowMs) { + const byRef = new Map(documents.map((document) => [document.ref, document])); + if (byRef.size !== documents.length) throw new Error("Native evidence store has duplicate refs"); + for (const document of documents) { + for (const dependency of document.dependencies) { + if (!byRef.has(dependency)) { + throw new Error(`Native evidence dependency is missing: ${dependency}`); + } + } + } + const rootClosure = closeDependencies(rootRefs, byRef); + const retainedSeeds = new Set(rootClosure); + for (const document of documents) { + if (nowMs - document.mtimeMs < NATIVE_EVIDENCE_RETENTION_POLICY.minimumAgeMs) { + retainedSeeds.add(document.ref); + } + } + for (const kind of MANAGED_KINDS) { + const newestUnreferenced = documents.filter((document) => document.kind === kind && !rootClosure.has(document.ref)).sort((left, right) => right.mtimeMs - left.mtimeMs || compareText8(left.ref, right.ref)).slice(0, NATIVE_EVIDENCE_RETENTION_POLICY.keepLatestUnreferencedPerKind); + for (const document of newestUnreferenced) retainedSeeds.add(document.ref); + } + const retained = closeDependencies(retainedSeeds, byRef); + const candidates = documents.filter( + (document) => !retained.has(document.ref) && nowMs - document.mtimeMs >= NATIVE_EVIDENCE_RETENTION_POLICY.minimumAgeMs + ).sort((left, right) => compareText8(left.ref, right.ref)); + return { + candidates: dependentsBeforeDependencies(candidates), + candidateBytes: candidates.reduce((total, candidate) => total + candidate.size, 0) + }; +} +function boundedRefs(refs) { + const shown = refs.slice(0, NATIVE_EVIDENCE_RETENTION_POLICY.maxReportedRefs); + const omitted = refs.length - shown.length; + return `${shown.join(", ")}${omitted > 0 ? `, ... (${omitted} more)` : ""}`; +} +function boundedMessage(message) { + const maximum = NATIVE_EVIDENCE_RETENTION_POLICY.maxReportMessageBytes; + if (Buffer.byteLength(message, "utf8") <= maximum) return message; + let result2 = message; + while (result2.length > 0 && Buffer.byteLength(`${result2}...`, "utf8") > maximum) { + result2 = result2.slice(0, -1); + } + return `${result2}...`; +} +function summaryFinding(name, plan, repaired, evidenceRoot) { + const refs = plan.candidates.map((candidate) => candidate.ref).sort(compareText8); + return { + severity: "info", + code: repaired ? "evidence-retention-cleaned" : "evidence-retention-candidates", + message: boundedMessage( + `${repaired ? "Removed" : "Found"} ${plan.candidates.length} old unreferenced Native evidence document(s) (${plan.candidateBytes} bytes) for ${name}: ${boundedRefs(refs)}` + ), + path: evidenceRoot + }; +} +async function restoreQuarantineIfSafe(quarantine, original) { + try { + await fs26.lstat(original); + return; + } catch (error) { + if (error.code !== "ENOENT") return; + } + try { + await fs26.rename(quarantine, original); + } catch { + } +} +async function deleteCandidate(candidate, hooks) { + await hooks.beforeDelete?.({ ref: candidate.ref, file: candidate.file }); + await verifyDirectoryChain7(candidate.directoryChain); + const beforeDocument = await readCanonicalDocument({ + changeRoot: candidate.changeRoot, + file: candidate.file, + ref: candidate.ref, + kind: candidate.kind, + hash: HASH_FILE_PATTERN.exec(path39.basename(candidate.file))[1], + name: candidate.change, + directoryChain: candidate.directoryChain + }); + const before = await fs26.lstat(candidate.file); + const beforeRealPath = await fs26.realpath(candidate.file); + if (!before.isFile() || before.isSymbolicLink() || !sameIdentity(candidate.identity, before) || !sameIdentity(candidate.identity, beforeDocument.identity) || !isInsidePath(candidate.directoryChain[0].realPath, beforeRealPath)) { + throw new Error(`Native evidence candidate changed before cleanup: ${candidate.ref}`); + } + const quarantine = path39.join( + path39.dirname(candidate.file), + `.${path39.basename(candidate.file)}.${randomUUID8()}.gc` + ); + await fs26.rename(candidate.file, quarantine); + try { + const [moved, movedRealPath] = await Promise.all([ + fs26.lstat(quarantine), + fs26.realpath(quarantine) + ]); + await verifyDirectoryChain7(candidate.directoryChain); + if (!moved.isFile() || moved.isSymbolicLink() || !sameContentVersionAfterLinkOrRename(candidate.identity, moved) || !isInsidePath(candidate.directoryChain[0].realPath, movedRealPath)) { + await restoreQuarantineIfSafe(quarantine, candidate.file); + throw new Error(`Native evidence candidate changed during cleanup: ${candidate.ref}`); + } + await fs26.rm(quarantine); + await verifyDirectoryChain7(candidate.directoryChain); + } catch (error) { + await restoreQuarantineIfSafe(quarantine, candidate.file); + throw error; + } +} +async function hasPendingRelocationOrArchive(paths) { + const config = await readProjectConfig(paths.projectRoot); + if (config?.native.pending_root_move) return true; + let entries; + try { + entries = await fs26.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) return true; + try { + const transaction = await readNativeTransaction(paths, entry2.name); + if (transaction.status !== "committed" && transaction.status !== "rolled-back") return true; + } catch { + return true; + } + } + return false; +} +async function changeNames(paths, requested) { + if (requested) return [requested]; + let entries; + try { + entries = await fs26.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + return entries.filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(compareText8); +} +async function retentionDeferred(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status !== "current" || !inspection.state || inspection.state.archived) + return true; + const [transition, migration, checkpoint] = await Promise.all([ + inspectPendingNativeTransition(paths, name), + inspectPendingNativeSchemaMigration(paths, name), + readNativeCheckpointJournal(paths, name) + ]); + return transition !== null || migration !== null || checkpoint !== null; +} +async function collectChangeRetention(options) { + if (await retentionDeferred(options.paths, options.name)) return []; + const inspection = await inspectNativeChange(options.paths, options.name); + if (inspection.status !== "current" || !inspection.state) return []; + if (!("implementation_scope" in inspection.state)) return []; + const cleanupRecovery = await inspectCleanupQuarantines({ + paths: options.paths, + name: options.name, + repair: options.repair, + hooks: options.hooks + }); + if (cleanupRecovery.pending) return cleanupRecovery.findings; + const rootRefs = []; + for (const value of [ + inspection.state.implementation_scope, + inspection.state.partial_allowance, + inspection.state.verification_evidence + ]) { + if (value !== null) rootRefs.push(value); + } + const documents = await scanEvidenceStore(options.paths, options.name); + const plan = planRetention(documents, rootRefs, options.nowMs); + if (plan.candidates.length === 0) return cleanupRecovery.findings; + const evidenceRef = evidenceRootRef(options.paths, options.name); + if (!options.repair) { + return [...cleanupRecovery.findings, summaryFinding(options.name, plan, false, evidenceRef)]; + } + const deleted = []; + for (const candidate of plan.candidates) { + try { + await deleteCandidate(candidate, options.hooks); + deleted.push(candidate); + } catch (error) { + const deletedBytes = deleted.reduce((total, item) => total + item.size, 0); + return [ + ...cleanupRecovery.findings, + ...deleted.length > 0 ? [ + summaryFinding( + options.name, + { candidates: deleted, candidateBytes: deletedBytes }, + true, + evidenceRef + ) + ] : [], + { + severity: "error", + code: "evidence-retention-cleanup-failed", + message: boundedMessage( + `Native evidence cleanup stopped safely for ${options.name}: ${error.message}` + ), + path: evidenceRef + } + ]; + } + } + return [...cleanupRecovery.findings, summaryFinding(options.name, plan, true, evidenceRef)]; +} +async function inspectNativeEvidenceRetention(options) { + if (options.deferAll || await hasPendingRelocationOrArchive(options.paths)) return []; + const now = options.now ?? /* @__PURE__ */ new Date(); + if (Number.isNaN(now.getTime())) throw new Error("Native evidence retention time is invalid"); + const run = async (repair) => { + const findings = []; + for (const name of await changeNames(options.paths, options.name)) { + try { + findings.push( + ...await collectChangeRetention({ + paths: options.paths, + name, + repair, + nowMs: now.getTime(), + hooks: options.hooks ?? {} + }) + ); + } catch (error) { + findings.push({ + severity: "error", + code: "evidence-retention-unsafe", + message: boundedMessage( + `Native evidence retention refused to clean ${name}: ${error.message}` + ), + path: evidenceRootRef(options.paths, name) + }); + } + } + return findings; + }; + if (!(options.repair ?? false)) return run(false); + const preflight = await run(false); + if (preflight.some((finding) => finding.severity === "error")) return preflight; + if (!preflight.some( + (finding) => finding.code === "evidence-retention-candidates" || finding.code === "evidence-retention-recovery-required" + )) { + return preflight; + } + try { + return await withNativeMutationLock(options.paths, "prune Native evidence", () => run(true)); + } catch (error) { + return [ + { + severity: "info", + code: "evidence-retention-cleanup-deferred", + message: boundedMessage( + `Native evidence cleanup was deferred until the project mutation lock is available: ${error.message}` + ) + } + ]; + } +} + +// domains/comet-native/native-root-move.ts +import { createHash as createHash14, randomUUID as randomUUID9 } from "crypto"; +import { promises as fs27 } from "fs"; +import path40 from "path"; +var NATIVE_ROOT_MOVE_MAX_FILE_BYTES = 64 * 1024 * 1024; +var NATIVE_ROOT_MOVE_MAX_JOURNAL_BYTES = 256 * 1024; +var NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES = 16 * 1024 * 1024; +async function exists3(target) { + try { + await fs27.access(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function assertNoUnfinishedTransactions(paths) { + let entries; + try { + entries = await fs27.readdir(paths.transactionsDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + for (const entry2 of entries) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let journal; + try { + journal = await readNativeTransaction(paths, entry2.name); + } catch (error) { + if (error.code === "ENOENT") { + throw new Error( + `Native transaction ${entry2.name} has no journal; run doctor before moving`, + { cause: error } + ); + } + throw error; + } + if (journal.status !== "committed" && journal.status !== "rolled-back") { + throw new Error(`Native transaction ${journal.id} is unfinished; recover it before moving`); + } + } +} +async function assertNoOtherLocks(paths, ownedLock) { + for (const entry2 of await fs27.readdir(paths.locksDir, { withFileTypes: true })) { + const file = path40.join(paths.locksDir, entry2.name); + if (path40.resolve(file) === path40.resolve(ownedLock)) continue; + if (entry2.isFile() || entry2.isSymbolicLink()) { + throw new Error(`Native lock must be diagnosed before moving the root: ${file}`); + } + } +} +async function refreshNativeWorkspaceIdentities(paths) { + let entries; + try { + entries = await fs27.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return; + throw error; + } + for (const entry2 of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let inspection; + try { + inspection = await inspectNativeChange(paths, entry2.name); + } catch { + continue; + } + if (inspection.status !== "current" || !inspection.state || !("revision" in inspection.state)) { + continue; + } + await writeNativeWorkspaceIdentity({ + paths, + name: entry2.name, + revision: inspection.state.revision + }); + } +} +async function walkTree(root, options) { + const treeEntries = []; + async function visit(directory) { + const protectedDirectory = await readNativeProtectedDirectory({ + root, + directory, + label: "Native root tree" + }); + const entries = protectedDirectory.entries; + entries.sort((left, right) => left.name.localeCompare(right.name)); + for (const entry2 of entries) { + await protectedDirectory.verify(); + const target = path40.join(directory, entry2.name); + if (options.excludedFiles?.has(path40.resolve(target))) { + await protectedDirectory.verify(); + continue; + } + const stat = await fs27.lstat(target); + if (entry2.isSymbolicLink() || stat.isSymbolicLink()) { + if (options.rejectSymlinks) throw new Error(`Native root contains a symlink: ${target}`); + await protectedDirectory.verify(); + continue; + } + if (entry2.isDirectory() && stat.isDirectory()) { + treeEntries.push({ + ref: path40.relative(root, target).split(path40.sep).join("/"), + type: "directory" + }); + await visit(target); + } else if (entry2.isFile() && stat.isFile()) { + const snapshot2 = await readNativeProtectedFile({ + root, + file: target, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + label: `Native root file ${path40.relative(root, target)}` + }); + treeEntries.push({ + ref: path40.relative(root, target).split(path40.sep).join("/"), + type: "file", + size: snapshot2.size, + hash: snapshot2.hash + }); + } else { + throw new Error(`Native root contains an unsupported file type: ${target}`); + } + await protectedDirectory.verify(); + } + } + await visit(root); + return treeEntries.sort((left, right) => left.ref.localeCompare(right.ref)); +} +async function copyTree(source, target, excludedFile, targetRoot) { + await ensureNativeProtectedDirectory({ + root: targetRoot, + directory: target, + label: "Native root move staging directory" + }); + async function copyDirectory(from, to) { + const protectedDirectory = await readNativeProtectedDirectory({ + root: source, + directory: from, + label: "Native root move source directory" + }); + const entries = protectedDirectory.entries; + for (const entry2 of entries) { + await protectedDirectory.verify(); + const sourceEntry = path40.join(from, entry2.name); + if (path40.resolve(sourceEntry) === path40.resolve(excludedFile)) { + await protectedDirectory.verify(); + continue; + } + const stat = await fs27.lstat(sourceEntry); + if (entry2.isSymbolicLink() || stat.isSymbolicLink()) { + throw new Error(`Native root contains a symlink: ${sourceEntry}`); + } + const targetEntry = path40.join(to, entry2.name); + if (entry2.isDirectory() && stat.isDirectory()) { + await ensureNativeProtectedDirectory({ + root: targetRoot, + directory: targetEntry, + label: "Native root move staging directory" + }); + await copyDirectory(sourceEntry, targetEntry); + } else if (entry2.isFile() && stat.isFile()) { + await copyNativeProtectedFile({ + sourceRoot: source, + source: sourceEntry, + targetRoot, + target: targetEntry, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + label: `Native root move file ${path40.relative(source, sourceEntry)}`, + exclusive: true, + expectedTargetHash: null + }); + } else { + throw new Error(`Native root contains an unsupported file type: ${sourceEntry}`); + } + await protectedDirectory.verify(); + } + } + await copyDirectory(source, target); +} +async function assertEquivalentTrees(source, target, excludedSourceLock, excludedTargetLock) { + const sourceFiles = await walkTree(source, { + rejectSymlinks: true, + excludedFiles: excludedSourceLock ? /* @__PURE__ */ new Set([path40.resolve(excludedSourceLock)]) : void 0 + }); + const targetFiles = await walkTree(target, { + rejectSymlinks: true, + excludedFiles: excludedTargetLock ? /* @__PURE__ */ new Set([path40.resolve(excludedTargetLock)]) : void 0 + }); + if (JSON.stringify(sourceFiles) !== JSON.stringify(targetFiles)) { + throw new Error( + `Native root copies differ; preserve both trees for manual recovery: ${source} and ${target}` + ); + } +} +function stagingDirectory(targetPaths, id) { + return path40.join(targetPaths.artifactRoot, `.comet-native-move-${id}`); +} +function sourceRemovalDirectory(sourcePaths, id) { + return path40.join(sourcePaths.artifactRoot, `.comet-native-source-${id}.removing`); +} +function stagingRemovalDirectory(staging) { + return `${staging}.removing`; +} +function rollbackRemovalDirectory(target, id) { + return path40.join(path40.dirname(target), `.${path40.basename(target)}.${id}.rollback-removing`); +} +function cleanupManifestFile(paths, id, kind) { + return path40.join(nativeTransactionPaths(paths, id).directory, `root-move-cleanup-${kind}.json`); +} +function cleanupManifestSource(manifest) { + return JSON.stringify(manifest, null, 2) + "\n"; +} +function cleanupManifestHash(source) { + return createHash14("sha256").update(source).digest("hex"); +} +function parseCleanupManifestEntry(value, index) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`Native root-move cleanup manifest entry ${index} must be an object`); + } + const entry2 = value; + const keys = Object.keys(entry2).sort(); + const expectedKeys = entry2.type === "file" ? ["hash", "ref", "size", "type"] : ["ref", "type"]; + if (JSON.stringify(keys) !== JSON.stringify(expectedKeys)) { + throw new Error(`Native root-move cleanup manifest entry ${index} has invalid fields`); + } + if (typeof entry2.ref !== "string" || entry2.ref.length === 0 || entry2.ref.includes("\\") || path40.posix.normalize(entry2.ref) !== entry2.ref || entry2.ref.startsWith("/") || entry2.ref.split("/").includes("..") || Buffer.byteLength(entry2.ref, "utf8") > 4096) { + throw new Error(`Native root-move cleanup manifest entry ${index} has an invalid ref`); + } + if (entry2.type === "directory") return { ref: entry2.ref, type: "directory" }; + if (entry2.type !== "file" || typeof entry2.size !== "number" || !Number.isSafeInteger(entry2.size) || entry2.size < 0 || typeof entry2.hash !== "string" || !/^[a-f0-9]{64}$/u.test(entry2.hash)) { + throw new Error(`Native root-move cleanup manifest entry ${index} is invalid`); + } + return { ref: entry2.ref, type: "file", size: entry2.size, hash: entry2.hash }; +} +function parseCleanupManifest(value, id, kind) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Native root-move cleanup manifest must be an object"); + } + const manifest = value; + if (JSON.stringify(Object.keys(manifest).sort()) !== JSON.stringify(["entries", "kind", "schema", "transactionId"]) || manifest.schema !== "comet.native.root-move-cleanup.v1" || manifest.transactionId !== id || manifest.kind !== kind || !Array.isArray(manifest.entries)) { + throw new Error("Native root-move cleanup manifest binding is invalid"); + } + const entries = manifest.entries.map(parseCleanupManifestEntry); + for (let index = 0; index < entries.length; index += 1) { + if (index > 0 && entries[index - 1].ref.localeCompare(entries[index].ref) >= 0) { + throw new Error("Native root-move cleanup manifest refs must be unique and sorted"); + } + const parent = path40.posix.dirname(entries[index].ref); + if (parent !== "." && !entries.some((candidate) => candidate.ref === parent && candidate.type === "directory")) { + throw new Error(`Native root-move cleanup manifest parent is missing: ${parent}`); + } + } + return { + schema: "comet.native.root-move-cleanup.v1", + transactionId: id, + kind, + entries + }; +} +async function writeCleanupManifest(options) { + const manifest = { + schema: "comet.native.root-move-cleanup.v1", + transactionId: options.id, + kind: options.kind, + entries: options.entries + }; + const source = cleanupManifestSource(manifest); + if (Buffer.byteLength(source, "utf8") > NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES) { + throw new Error("Native root-move cleanup manifest exceeds its byte budget"); + } + await atomicWriteText(cleanupManifestFile(options.paths, options.id, options.kind), source, { + containedRoot: options.paths.nativeRoot + }); + return cleanupManifestHash(source); +} +async function readCleanupManifest(options) { + const snapshot2 = await readNativeProtectedFile({ + root: options.paths.nativeRoot, + file: cleanupManifestFile(options.paths, options.id, options.cleanup.kind), + maxBytes: NATIVE_ROOT_MOVE_MAX_MANIFEST_BYTES, + label: `Native root-move cleanup manifest ${options.cleanup.kind}` + }); + if (snapshot2.hash !== options.cleanup.manifestHash) { + throw new Error("Native root-move cleanup manifest hash changed"); + } + return parseCleanupManifest( + JSON.parse(snapshot2.bytes.toString("utf8")), + options.id, + options.cleanup.kind + ); +} +async function assertCleanupManifestMatch(options) { + const current = await walkTree(options.quarantine, { rejectSymlinks: true }); + const expected = new Map(options.manifest.entries.map((entry2) => [entry2.ref, entry2])); + for (const entry2 of current) { + const bound = expected.get(entry2.ref); + if (!bound || JSON.stringify(bound) !== JSON.stringify(entry2)) { + throw new Error( + `Native root-move cleanup quarantine differs from its bound manifest: ${entry2.ref}` + ); + } + } + if (options.exact && current.length !== options.manifest.entries.length) { + throw new Error("Native root-move cleanup quarantine is incomplete before deletion"); + } + return current; +} +function treeEntryDepth(entry2) { + return entry2.ref.split("/").length; +} +async function deleteCleanupManifestSubset(options) { + const current = await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest: options.manifest, + exact: false + }); + const ordered = [...current].sort( + (left, right) => treeEntryDepth(right) - treeEntryDepth(left) || (left.type === right.type ? right.ref.localeCompare(left.ref) : left.type === "file" ? -1 : 1) + ); + let removedCount = 0; + for (const entry2 of ordered) { + const target = path40.join(options.quarantine, ...entry2.ref.split("/")); + if (entry2.type === "file") { + await removeNativeProtectedFile({ + root: options.projectRoot, + file: target, + maxBytes: NATIVE_ROOT_MOVE_MAX_FILE_BYTES, + expectedHash: entry2.hash, + expectedSize: entry2.size, + label: `Native root-move cleanup file ${entry2.ref}` + }); + } else { + await removeNativeProtectedEmptyDirectory({ + root: options.projectRoot, + directory: target, + label: `Native root-move cleanup directory ${entry2.ref}` + }); + } + removedCount += 1; + await options.hooks?.afterRootMoveCleanupEntryRemoved?.( + options.manifest.kind, + entry2.ref, + removedCount + ); + } + await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest: options.manifest, + exact: false + }); + await removeNativeProtectedEmptyDirectory({ + root: options.projectRoot, + directory: options.quarantine, + label: `Native root-move cleanup quarantine ${options.manifest.kind}` + }); +} +function pendingConfig(config, pending, activeArtifactRoot = config.native.artifact_root) { + return { + ...config, + native: { ...config.native, artifact_root: activeArtifactRoot, pending_root_move: pending } + }; +} +function rootMoveJournal(options) { + return { + schema: "comet.native.transaction.v1", + id: options.id, + kind: "root-move", + status: "prepared", + projectRoot: options.paths.projectRoot, + nativeRoot: options.paths.nativeRoot, + createdAt: options.now.toISOString(), + operations: [] + }; +} +async function readRootMoveJournal(sourcePaths, destinationPaths, stage, id) { + for (const paths of [sourcePaths, destinationPaths]) { + try { + const journal = await readNativeTransaction(paths, id); + if (journal.kind !== "root-move") throw new Error(`Transaction ${id} is not a root move`); + return { journal, paths }; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + } + const stageJournal = path40.join(stage, "runtime", "transactions", id, "transaction.json"); + try { + const snapshot2 = await readNativeProtectedFile({ + root: stage, + file: stageJournal, + maxBytes: NATIVE_ROOT_MOVE_MAX_JOURNAL_BYTES, + label: `Staged Native root-move journal ${id}` + }); + const journal = JSON.parse(snapshot2.bytes.toString("utf8")); + if (journal.schema !== "comet.native.transaction.v1" || journal.kind !== "root-move") { + throw new Error(`Invalid staged root-move journal: ${id}`); + } + return { journal, paths: destinationPaths }; + } catch (error) { + if (error.code !== "ENOENT") throw error; + throw new Error(`Native root-move journal is missing: ${id}`, { cause: error }); + } +} +async function setPendingStage(options) { + const updated = pendingConfig( + options.config, + { ...options.pending, stage: options.stage }, + options.activeArtifactRoot + ); + await writeProjectConfig(options.projectRoot, updated); + return updated; +} +async function setPendingCleanup(options) { + const pending = options.config.native.pending_root_move; + if (!pending || pending.id !== options.id) { + throw new Error(`Native root-move cleanup ${options.id} lost its pending configuration`); + } + const updated = pendingConfig( + options.config, + { + id: pending.id, + fromArtifactRoot: pending.fromArtifactRoot, + toArtifactRoot: pending.toArtifactRoot, + stage: pending.stage, + ...options.cleanup ? { cleanup: options.cleanup } : {} + }, + options.config.native.artifact_root + ); + await writeProjectConfig(options.projectRoot, updated); + return updated; +} +async function cleanupRootMoveTree(options) { + let config = options.config; + const pending = config.native.pending_root_move; + if (!pending || pending.id !== options.id) { + throw new Error(`Native root-move cleanup ${options.id} lost its pending configuration`); + } + let cleanup = pending.cleanup; + if (cleanup && cleanup.kind !== options.kind) { + throw new Error( + `Native root move has unfinished ${cleanup.kind} cleanup; cannot start ${options.kind}` + ); + } + let targetExists = await exists3(options.target); + let quarantineExists = await exists3(options.quarantine); + if (targetExists && quarantineExists) { + throw new Error(`Native root-move cleanup target and quarantine both exist: ${options.kind}`); + } + if (!cleanup) { + if (!targetExists && !quarantineExists) return config; + if (!targetExists && quarantineExists) { + throw new Error( + `Native root-move cleanup quarantine is not transaction-bound: ${options.kind}` + ); + } + const manifestHash = await writeCleanupManifest({ + paths: options.stablePaths, + id: options.id, + kind: options.kind, + entries: await walkTree(options.target, { rejectSymlinks: true }) + }); + cleanup = { kind: options.kind, state: "prepared", manifestHash }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + const manifest = await readCleanupManifest({ + paths: options.stablePaths, + id: options.id, + cleanup + }); + targetExists = await exists3(options.target); + quarantineExists = await exists3(options.quarantine); + if (targetExists && quarantineExists) { + throw new Error(`Native root-move cleanup target and quarantine both exist: ${options.kind}`); + } + if (targetExists) { + if (cleanup.state !== "prepared") { + throw new Error( + `Native root-move cleanup target reappeared after quarantine: ${options.kind}` + ); + } + await quarantineNativeProtectedDirectory({ + root: options.projectRoot, + directory: options.target, + quarantine: options.quarantine, + label: options.label, + beforeQuarantine: options.beforeQuarantine, + afterQuarantine: options.afterQuarantine + }); + quarantineExists = true; + } + if (!quarantineExists) { + if (cleanup.state !== "deleting") { + throw new Error(`Native root-move cleanup quarantine disappeared: ${options.kind}`); + } + return setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id + }); + } + await assertCleanupManifestMatch({ + quarantine: options.quarantine, + manifest, + exact: cleanup.state !== "deleting" + }); + if (cleanup.state === "prepared") { + cleanup = { ...cleanup, state: "quarantined" }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + if (cleanup.state === "quarantined") { + cleanup = { ...cleanup, state: "deleting" }; + config = await setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id, + cleanup + }); + } + await deleteCleanupManifestSubset({ + projectRoot: options.projectRoot, + quarantine: options.quarantine, + manifest, + hooks: options.hooks + }); + return setPendingCleanup({ + projectRoot: options.projectRoot, + config, + id: options.id + }); +} +async function finishForwardMove(options) { + let config = options.config; + let stage = config.native.pending_root_move.stage; + if (stage === "copying") { + if (!await exists3(options.sourcePaths.nativeRoot)) { + throw new Error(`Native source root is missing: ${options.sourcePaths.nativeRoot}`); + } + const stagingRemoval = stagingRemovalDirectory(options.staging); + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: options.pending.id, + kind: "restart-staging", + stablePaths: options.sourcePaths, + target: options.staging, + quarantine: stagingRemoval, + label: "Native root move stale staging removal", + hooks: options.hooks + }); + await walkTree(options.sourcePaths.nativeRoot, { + rejectSymlinks: true, + excludedFiles: /* @__PURE__ */ new Set([path40.resolve(options.lockFile)]) + }); + await copyTree( + options.sourcePaths.nativeRoot, + options.staging, + options.lockFile, + options.destinationPaths.projectRoot + ); + await assertEquivalentTrees(options.sourcePaths.nativeRoot, options.staging, options.lockFile); + config = await setPendingStage({ + projectRoot: options.projectRoot, + config, + pending: options.pending, + stage: "ready" + }); + stage = "ready"; + await options.hooks?.afterRootMoveStage?.("ready", options.journal); + } + if (stage === "ready") { + if (await exists3(options.destinationPaths.nativeRoot)) { + if (await exists3(options.staging)) { + throw new Error(`Native destination is occupied: ${options.destinationPaths.nativeRoot}`); + } + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.destinationPaths.nativeRoot, + options.lockFile + ); + } else { + if (!await exists3(options.staging)) throw new Error(`Native move staging tree is missing`); + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.staging, + options.lockFile + ); + await fs27.rename(options.staging, options.destinationPaths.nativeRoot); + } + config = await setPendingStage({ + projectRoot: options.projectRoot, + config, + pending: options.pending, + stage: "switched", + activeArtifactRoot: options.pending.toArtifactRoot + }); + stage = "switched"; + await options.hooks?.afterRootMoveStage?.("switched", options.journal); + } + if (stage !== "switched") throw new Error(`Unsupported Native root-move stage: ${stage}`); + if (!await exists3(options.destinationPaths.nativeRoot)) { + throw new Error(`Native destination root is missing: ${options.destinationPaths.nativeRoot}`); + } + const sourceRemoval = sourceRemovalDirectory(options.sourcePaths, options.pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(options.sourcePaths.nativeRoot)) { + await assertEquivalentTrees( + options.sourcePaths.nativeRoot, + options.destinationPaths.nativeRoot, + options.lockFile + ); + } + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: options.pending.id, + kind: "forward-source", + stablePaths: options.destinationPaths, + target: options.sourcePaths.nativeRoot, + quarantine: sourceRemoval, + label: "Native root move source removal", + beforeQuarantine: () => options.hooks?.beforeRootMoveSourceRemove?.(options.sourcePaths.nativeRoot), + afterQuarantine: (quarantine) => options.hooks?.afterRootMoveSourceQuarantined?.(quarantine), + hooks: options.hooks + }); + await refreshNativeWorkspaceIdentities(options.destinationPaths); + const destinationJournal = await readNativeTransaction( + options.destinationPaths, + options.pending.id + ); + await finalizeNativeTransaction(options.destinationPaths, destinationJournal, "commit"); + const stableNative = { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot + }; + const committed = { + ...config, + native: { ...stableNative, artifact_root: options.pending.toArtifactRoot } + }; + await writeProjectConfig(options.projectRoot, committed); + return committed; +} +async function moveNativeRoot(options) { + const current = await readProjectConfig(options.projectRoot) ?? defaultProjectConfig("docs"); + if (current.native.pending_root_move) { + throw new Error( + `Native root move ${current.native.pending_root_move.id} is already incomplete` + ); + } + const toArtifactRoot = normalizeArtifactRootRef(options.toArtifactRoot); + if (toArtifactRoot === current.native.artifact_root) { + throw new Error(`Native artifact root is already ${toArtifactRoot}`); + } + const sourcePaths = await nativeProjectPaths(options.projectRoot, current.native.artifact_root); + const destinationPaths = await nativeProjectPaths(options.projectRoot, toArtifactRoot); + if (isInsidePath(sourcePaths.nativeRoot, destinationPaths.nativeRoot) || isInsidePath(destinationPaths.nativeRoot, sourcePaths.nativeRoot)) { + throw new Error("Native source and destination roots must not overlap"); + } + if (!await exists3(sourcePaths.nativeRoot)) { + throw new Error(`Native source root does not exist: ${sourcePaths.nativeRoot}`); + } + await assertNoUnfinishedTransactions(sourcePaths); + if (await exists3(destinationPaths.nativeRoot)) { + throw new Error(`Native destination is occupied: ${destinationPaths.nativeRoot}`); + } + const lock = await acquireNativeLock(sourcePaths, "root-move", `move root to ${toArtifactRoot}`); + const id = randomUUID9(); + const pending = { + id, + fromArtifactRoot: current.native.artifact_root, + toArtifactRoot, + stage: "copying" + }; + const journal = rootMoveJournal({ id, paths: sourcePaths, now: options.now ?? /* @__PURE__ */ new Date() }); + const staging = stagingDirectory(destinationPaths, id); + try { + await assertNoOtherLocks(sourcePaths, lock.file); + if (await exists3(staging)) throw new Error(`Native move staging path is occupied: ${staging}`); + await writeProjectConfig(options.projectRoot, pendingConfig(current, pending)); + await createNativeTransaction(sourcePaths, journal); + await options.hooks?.afterRootMoveStage?.("copying", journal); + await finishForwardMove({ + projectRoot: options.projectRoot, + config: pendingConfig(current, pending), + pending, + sourcePaths, + destinationPaths, + staging, + journal, + lockFile: lock.file, + hooks: options.hooks + }); + return { + fromNativeRoot: sourcePaths.nativeRoot, + toNativeRoot: destinationPaths.nativeRoot, + transactionId: id + }; + } finally { + await releaseNativeLock(lock); + } +} +async function recoverNativeRootMove(options) { + let config = await readProjectConfig(options.projectRoot); + const pending = config?.native.pending_root_move; + if (!config || !pending) throw new Error("No pending Native root move was found"); + const sourcePaths = await nativeProjectPaths(options.projectRoot, pending.fromArtifactRoot); + const destinationPaths = await nativeProjectPaths(options.projectRoot, pending.toArtifactRoot); + const staging = stagingDirectory(destinationPaths, pending.id); + const lockPaths = await exists3(sourcePaths.nativeRoot) ? sourcePaths : destinationPaths; + const lock = await acquireNativeLock(lockPaths, "root-move", `recover root ${pending.id}`); + try { + let journalInfo; + try { + journalInfo = await readRootMoveJournal(sourcePaths, destinationPaths, staging, pending.id); + } catch (error) { + if (pending.stage !== "copying" || !await exists3(sourcePaths.nativeRoot)) throw error; + const journal = rootMoveJournal({ id: pending.id, paths: sourcePaths, now: /* @__PURE__ */ new Date() }); + await createNativeTransaction(sourcePaths, journal); + journalInfo = { journal, paths: sourcePaths }; + } + if (options.strategy === "continue") { + const committed = await finishForwardMove({ + projectRoot: options.projectRoot, + config, + pending, + sourcePaths, + destinationPaths, + staging, + journal: journalInfo.journal, + lockFile: lock.file, + hooks: options.hooks + }); + return { activeNativeRoot: destinationPaths.nativeRoot, config: committed }; + } + if (!await exists3(sourcePaths.nativeRoot)) { + throw new Error("Cannot roll back after the old Native root was removed; continue recovery"); + } + const destinationRemoval = rollbackRemovalDirectory(destinationPaths.nativeRoot, pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(destinationPaths.nativeRoot)) { + await assertEquivalentTrees(sourcePaths.nativeRoot, destinationPaths.nativeRoot, lock.file); + } + if (!config.native.pending_root_move?.cleanup || config.native.pending_root_move.cleanup.kind === "rollback-destination") { + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: pending.id, + kind: "rollback-destination", + stablePaths: sourcePaths, + target: destinationPaths.nativeRoot, + quarantine: destinationRemoval, + label: "Native root move rollback destination removal", + hooks: options.hooks + }); + } + const stagingRemoval = rollbackRemovalDirectory(staging, pending.id); + if (!config.native.pending_root_move?.cleanup && await exists3(staging)) { + await assertEquivalentTrees(sourcePaths.nativeRoot, staging, lock.file); + } + if (!config.native.pending_root_move?.cleanup || config.native.pending_root_move.cleanup.kind === "rollback-staging") { + config = await cleanupRootMoveTree({ + projectRoot: options.projectRoot, + config, + id: pending.id, + kind: "rollback-staging", + stablePaths: sourcePaths, + target: staging, + quarantine: stagingRemoval, + label: "Native root move rollback staging removal", + hooks: options.hooks + }); + } + const sourceJournal = await readNativeTransaction(sourcePaths, pending.id); + await rollbackNativeTransaction(sourcePaths, sourceJournal); + const stableNative = { + artifact_root: config.native.artifact_root, + language: config.native.language, + clarification_mode: config.native.clarification_mode, + snapshot: config.native.snapshot + }; + const restored = { + ...config, + native: { ...stableNative, artifact_root: pending.fromArtifactRoot } + }; + await writeProjectConfig(options.projectRoot, restored); + return { activeNativeRoot: sourcePaths.nativeRoot, config: restored }; + } finally { + await releaseNativeLock(lock); + } +} + +// domains/comet-native/native-doctor.ts +var NATIVE_DOCTOR_MAX_CHANGE_ENTRIES = 4096; +var NATIVE_DOCTOR_MAX_TRANSACTION_ENTRIES = 4096; +var NATIVE_DOCTOR_MAX_LOCK_ENTRIES = 1024; +async function directoryEntries(paths, directory, maxEntries) { + try { + const protectedDirectory = await readNativeProtectedDirectory({ + root: paths.nativeRoot, + directory, + label: "Native doctor directory", + maxEntries + }); + await protectedDirectory.verify(); + return protectedDirectory.entries; + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } +} +async function clearStaleRecoveryLocks(targets, findings) { + const unique = new Map( + targets.map((target) => [ + path41.resolve(target.file), + { ...target, file: path41.resolve(target.file) } + ]) + ); + for (const { paths, file } of unique.values()) { + let diagnosis; + try { + diagnosis = await diagnoseNativeLock(file); + } catch (error) { + findings.push({ + severity: "error", + code: "lock-invalid", + message: `Native recovery lock is invalid: ${error.message}`, + path: file + }); + return false; + } + if (diagnosis.status === "missing") continue; + if (diagnosis.status === "stale") { + const takeover = await takeOverNativeStaleLock(paths, file, diagnosis); + if (takeover.status === "removed") { + findings.push({ + severity: "info", + code: "stale-recovery-lock-removed", + message: `Removed stale lock before explicit transaction recovery`, + path: file + }); + continue; + } + if (takeover.status === "missing") continue; + diagnosis = takeover.diagnosis; + } + if (diagnosis.status === "stale") { + findings.push({ + severity: "error", + code: "lock-takeover-raced", + message: "Native recovery lock changed while doctor was preparing stale takeover", + path: file + }); + return false; + } + findings.push({ + severity: "error", + code: diagnosis.status === "active" ? "lock-active" : "lock-owner-unknown", + message: diagnosis.status === "active" ? `Native recovery lock is still owned by a live process` : `Native recovery lock owner cannot be proven stale`, + path: file + }); + return false; + } + return true; +} +async function inspectSelection(paths, repair) { + const file = nativeSelectionFile(paths); + let value; + try { + await resolveContainedNativePath(paths.projectRoot, file); + const selection = await readNativeSelectionRecord(paths); + if (!selection) return []; + value = selection; + } catch (error) { + if (error.code === "ENOENT") return []; + return [ + { + severity: "error", + code: "selection-invalid", + message: `Native selection is invalid: ${error.message}`, + path: file + } + ]; + } + if (value.schema !== "comet.selection.v2" || value.workflow !== "native" || typeof value.change !== "string") { + return [ + { + severity: "error", + code: "selection-invalid", + message: "Native selection has an invalid schema or change name", + path: file + } + ]; + } + try { + await readNativeChange(paths, value.change); + return []; + } catch (error) { + if (error.code !== "ENOENT") { + return [ + { + severity: "error", + code: "selection-target-invalid", + message: `Selected Native change is invalid: ${error.message}`, + path: file + } + ]; + } + } + if (repair) { + await fs28.rm(file, { force: true }); + return [ + { + severity: "info", + code: "selection-cleared", + message: `Cleared stale Native selection ${value.change}`, + path: file + } + ]; + } + return [ + { + severity: "warning", + code: "selection-stale", + message: `Selected Native change does not exist: ${value.change}`, + path: file + } + ]; +} +async function inspectManagedPaths(paths) { + const findings = []; + for (const managedPath of [ + paths.specsDir, + paths.changesDir, + paths.archiveDir, + paths.runtimeDir, + paths.locksDir, + paths.transactionsDir + ]) { + try { + await resolveContainedNativePath(paths.nativeRoot, managedPath); + } catch (error) { + findings.push({ + severity: "error", + code: "native-path-unsafe", + message: `Managed Native path is unsafe: ${error.message}`, + path: managedPath + }); + } + } + return findings; +} +async function inspectTransactions(paths, options) { + const findings = []; + const unfinished = []; + for (const entry2 of await directoryEntries( + paths, + paths.transactionsDir, + NATIVE_DOCTOR_MAX_TRANSACTION_ENTRIES + )) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + let journal; + try { + journal = await readNativeTransaction(paths, entry2.name); + } catch (error) { + findings.push({ + severity: "error", + code: "transaction-invalid", + message: `Native transaction ${entry2.name} is invalid: ${error.message}`, + path: path41.join(paths.transactionsDir, entry2.name) + }); + continue; + } + if (journal.status === "committed" || journal.status === "rolled-back") continue; + if (options.name && journal.change && journal.change !== options.name) continue; + if (journal.kind !== "archive") { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "root-move-transaction-orphaned", + message: `Root-move transaction ${journal.id} is incomplete but project config has no matching pending move` + }); + continue; + } + if (options.repair && options.recoveryStrategy) { + try { + const locksReady = await withNativeLockRecovery( + [paths], + `doctor archive recovery ${journal.id}`, + async () => { + const ready = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, "archive.lock") } + ], + findings + ); + if (!ready) return false; + await recoverArchiveTransaction({ + paths, + transactionId: journal.id, + strategy: options.recoveryStrategy + }); + return true; + } + ); + if (!locksReady) { + unfinished.push(journal); + continue; + } + findings.push({ + severity: "info", + code: "archive-transaction-recovered", + message: `${options.recoveryStrategy === "continue" ? "Continued" : "Rolled back"} archive transaction ${journal.id}` + }); + } catch (error) { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "archive-recovery-failed", + message: `Archive recovery failed: ${error.message}` + }); + } + } else { + unfinished.push(journal); + findings.push({ + severity: "error", + code: "archive-transaction-incomplete", + message: options.repair ? `Archive transaction ${journal.id} needs an explicit recovery strategy` : `Archive transaction ${journal.id} is incomplete`, + repair: options.recoveryStrategy ?? "continue" + }); + } + } + return { findings, unfinished }; +} +async function inspectLocks(paths, repair, unfinished) { + const findings = []; + for (const entry2 of await directoryEntries( + paths, + paths.locksDir, + NATIVE_DOCTOR_MAX_LOCK_ENTRIES + )) { + if (!entry2.isFile() || entry2.isSymbolicLink() || !entry2.name.endsWith(".lock")) continue; + const file = path41.join(paths.locksDir, entry2.name); + try { + const diagnosis = await diagnoseNativeLock(file); + if (diagnosis.status === "active") { + findings.push({ + severity: "warning", + code: "lock-active", + message: `Native lock is active for ${diagnosis.owner?.operation ?? "an operation"}`, + path: file + }); + } else if (diagnosis.status === "unknown") { + findings.push({ + severity: "warning", + code: "lock-owner-unknown", + message: "Native lock owner cannot be proven stale", + path: file + }); + } else if (diagnosis.status === "stale") { + if (repair && unfinished.length === 0) { + const takeover = await takeOverNativeStaleLock(paths, file, diagnosis); + if (takeover.status === "removed") { + findings.push({ + severity: "info", + code: "stale-lock-removed", + message: "Removed a Native lock whose local owner process is absent", + path: file + }); + } else if (takeover.status === "changed") { + findings.push({ + severity: takeover.diagnosis.status === "active" ? "warning" : "error", + code: "lock-takeover-raced", + message: "Native lock changed while doctor was preparing stale takeover", + path: file + }); + } + } else { + findings.push({ + severity: unfinished.length > 0 ? "error" : "warning", + code: "lock-stale", + message: unfinished.length > 0 ? "Native lock is stale but an unfinished transaction still requires recovery" : "Native lock owner process is absent", + path: file + }); + } + } + } catch (error) { + findings.push({ + severity: "error", + code: "lock-invalid", + message: `Native lock metadata is invalid: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function inspectChanges(paths, name) { + const findings = []; + const statuses = name ? await listNativeStatus(paths).then((all) => all.filter((status) => status.name === name)) : await listNativeStatus(paths); + if (name && statuses.length === 0) { + return [ + { + severity: "error", + code: "change-missing", + message: `Native change does not exist: ${name}` + } + ]; + } + for (const status of statuses) { + if (status.migrationRequired) continue; + if (status.phase === "invalid") { + findings.push({ + severity: "error", + code: "change-invalid", + message: status.error ?? `Native change ${status.name} is invalid`, + path: path41.join(paths.changesDir, status.name, NATIVE_CHANGE_STATE_FILE) + }); + continue; + } + const detailed = await inspectNativeStatus(paths, status.name, { details: true }); + for (const artifact of detailed.findings ?? []) { + if (artifact.code === "trajectory-tail-incomplete" || artifact.code === "checkpoint-progress-incomplete") { + continue; + } + findings.push({ + severity: artifact.severity, + code: artifact.code, + message: `${status.name}: ${artifact.message}`, + ...artifact.path ? { path: path41.join(paths.projectRoot, artifact.path) } : {} + }); + } + } + return findings; +} +async function inspectTrajectoryTailRepairs(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + try { + const inspection = await inspectNativeTrajectoryTail(paths, name); + if (inspection.status !== "repairable") continue; + if (!options.repair) { + findings.push({ + severity: "error", + code: "trajectory-tail-incomplete", + message: `Native trajectory for ${name} has an incomplete final line ${inspection.line}; ${inspection.discardedBytes} byte(s) are outside the last complete event`, + path: inspection.file, + repair: "truncate-tail" + }); + continue; + } + const repaired = await repairNativeTrajectoryTail(paths, name); + if (repaired) { + findings.push({ + severity: "info", + code: "trajectory-tail-repaired", + message: `Removed the incomplete Native trajectory tail for ${name} and preserved all complete events`, + path: repaired.file + }); + } + } catch (error) { + findings.push({ + severity: "error", + code: "trajectory-tail-repair-failed", + message: `Native trajectory tail repair failed for ${name}: ${error.message}`, + path: path41.join(paths.changesDir, name, "runtime", "trajectory.jsonl") + }); + } + } + return findings; +} +async function inspectWorkspaceIdentityMigrations(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + try { + if (!await nativeWorkspaceIdentityNeedsMigration(paths, name)) { + continue; + } + if (!options.repair) { + findings.push({ + severity: "warning", + code: "workspace-identity-migration-required", + message: `Native workspace identity for ${name} uses legacy external-probe or hash-only metadata`, + path: nativeWorkspaceFile(paths, name), + repair: "migrate" + }); + continue; + } + const state = await readNativeChange(paths, name); + const migrated = await migrateLegacyNativeWorkspaceIdentity({ + paths, + name, + revision: state.revision + }); + if (migrated) { + findings.push({ + severity: "info", + code: "workspace-identity-migrated", + message: `Replaced legacy workspace metadata for ${name} with process-free root identities`, + path: nativeWorkspaceFile(paths, name) + }); + } + } catch (error) { + findings.push({ + severity: "error", + code: "workspace-identity-migration-failed", + message: `Native workspace identity migration failed for ${name}: ${error.message}`, + path: nativeWorkspaceFile(paths, name) + }); + } + } + return findings; +} +async function inspectSchemaMigrations(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + const file = nativeSchemaMigrationJournalFile(paths, name); + try { + const pending = await inspectPendingNativeSchemaMigration(paths, name); + const inspection = await inspectNativeChange(paths, name); + if (!pending && inspection.status === "current") continue; + if (inspection.status === "runtime-incompatible") { + findings.push({ + severity: "error", + code: "change-runtime-incompatible", + message: inspection.message ?? `Native change ${name} requires a newer runtime`, + path: path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE) + }); + continue; + } + if (!options.repair) { + findings.push({ + severity: "error", + code: pending ? "schema-migration-incomplete" : "schema-migration-required", + message: pending ? `Native schema migration ${pending.id} is incomplete for ${name}` : `Native change ${name} requires migration to the current schema`, + path: pending ? file : path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE), + repair: "migrate" + }); + continue; + } + await migrateNativeChange({ paths, name }); + findings.push({ + severity: "info", + code: pending ? "schema-migration-recovered" : "schema-migrated", + message: `Migrated Native change ${name} to the current schema`, + path: path41.join(paths.changesDir, name, NATIVE_CHANGE_STATE_FILE) + }); + } catch (error) { + if (error.code === "ENOENT") continue; + findings.push({ + severity: "error", + code: "schema-migration-failed", + message: `Native schema migration failed for ${name}: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function inspectTransitionJournals(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + let journal; + try { + journal = await inspectPendingNativeTransition(paths, name); + } catch (error) { + if (error instanceof NativeTransitionMigrationRequiredError) continue; + findings.push({ + severity: "error", + code: "transition-invalid", + message: `Native transition journal is invalid: ${error.message}`, + path: nativeTransitionJournalFile(paths, name) + }); + continue; + } + if (!journal) continue; + if (options.repair && options.recoveryStrategy === "continue") { + try { + const recovered = await withNativeLockRecovery( + [paths], + `doctor transition recovery ${name}`, + async () => { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, `transition-${name}.lock`) } + ], + findings + ); + if (!locksReady) return false; + await continueNativeTransition(paths, name); + return true; + } + ); + if (!recovered) continue; + findings.push({ + severity: "info", + code: "transition-recovered", + message: `Continued Native phase transition ${journal.id} for ${name}`, + path: nativeTransitionJournalFile(paths, name) + }); + } catch (error) { + findings.push({ + severity: "error", + code: "transition-recovery-failed", + message: `Native transition recovery failed: ${error.message}`, + path: nativeTransitionJournalFile(paths, name) + }); + } + continue; + } + findings.push({ + severity: "error", + code: "transition-incomplete", + message: options.repair && options.recoveryStrategy === "rollback" ? `Native phase transition ${journal.id} only supports deterministic continue recovery` : `Native phase transition ${journal.id} is incomplete for ${name}`, + path: nativeTransitionJournalFile(paths, name), + repair: "continue" + }); + } + return findings; +} +async function inspectCheckpointJournals(paths, options) { + const findings = []; + const names = options.name ? [options.name] : (await directoryEntries(paths, paths.changesDir, NATIVE_DOCTOR_MAX_CHANGE_ENTRIES)).filter((entry2) => entry2.isDirectory() && !entry2.isSymbolicLink()).map((entry2) => entry2.name).sort(); + for (const name of names) { + const file = nativeCheckpointJournalFile(paths, name); + let journal; + try { + journal = await readNativeCheckpointJournal(paths, name); + } catch (error) { + if (error.code === "ENOENT") continue; + findings.push({ + severity: "error", + code: "checkpoint-progress-invalid", + message: `Native progress checkpoint journal is invalid: ${error.message}`, + path: file + }); + continue; + } + if (!journal) continue; + if (!options.repair) { + findings.push({ + severity: "error", + code: "checkpoint-progress-incomplete", + message: `Native progress checkpoint ${journal.id} is incomplete for ${name}`, + path: file + }); + continue; + } + try { + const recovered = await withNativeLockRecovery( + [paths], + `doctor checkpoint recovery ${name}`, + async () => { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths, file: path41.join(paths.locksDir, "root-move.lock") }, + { paths, file: path41.join(paths.locksDir, `transition-${name}.lock`) } + ], + findings + ); + if (!locksReady) return false; + await continueNativeCheckpoint(paths, name); + return true; + } + ); + if (!recovered) continue; + findings.push({ + severity: "info", + code: "checkpoint-progress-recovered", + message: `Continued Native progress checkpoint ${journal.id} for ${name}`, + path: file + }); + } catch (error) { + findings.push({ + severity: "error", + code: "checkpoint-progress-recovery-failed", + message: `Native progress checkpoint recovery failed: ${error.message}`, + path: file + }); + } + } + return findings; +} +async function doctorNativeProject(options) { + const repair = options.repair ?? false; + const findings = []; + let paths = options.paths; + let config; + try { + config = await readProjectConfig(paths.projectRoot); + } catch (error) { + const result2 = { + healthy: false, + findings: [ + { + severity: "error", + code: "config-invalid", + message: `Comet project config is invalid: ${error.message}`, + path: paths.configFile + } + ] + }; + return result2; + } + let relocationRecoveryPending = Boolean(config?.native.pending_root_move); + if (config?.native.pending_root_move) { + const pending = config.native.pending_root_move; + const [fromPaths, toPaths] = await Promise.all([ + nativeProjectPaths(paths.projectRoot, pending.fromArtifactRoot), + nativeProjectPaths(paths.projectRoot, pending.toArtifactRoot) + ]); + if (repair && options.recoveryStrategy) { + try { + const locksReady = await clearStaleRecoveryLocks( + [ + { paths: fromPaths, file: path41.join(fromPaths.locksDir, "root-move.lock") }, + { paths: toPaths, file: path41.join(toPaths.locksDir, "root-move.lock") } + ], + findings + ); + if (!locksReady) return { healthy: false, findings }; + const recovered = await recoverNativeRootMove({ + projectRoot: paths.projectRoot, + strategy: options.recoveryStrategy + }); + paths = await nativeProjectPaths(paths.projectRoot, recovered.config.native.artifact_root); + relocationRecoveryPending = false; + findings.push({ + severity: "info", + code: "root-move-recovered", + message: `${options.recoveryStrategy === "continue" ? "Continued" : "Rolled back"} Native root move ${pending.id}` + }); + } catch (error) { + findings.push({ + severity: "error", + code: "root-move-recovery-failed", + message: `Native root recovery failed: ${error.message}` + }); + return { healthy: false, findings }; + } + } else { + findings.push({ + severity: "error", + code: "root-move-incomplete", + message: `Native root move ${pending.id} is ${pending.stage}; inspect ${fromPaths.nativeRoot} and ${toPaths.nativeRoot}`, + repair: options.recoveryStrategy ?? "continue" + }); + } + } + const managedPathFindings = await inspectManagedPaths(paths); + findings.push(...managedPathFindings); + if (managedPathFindings.length > 0) return { healthy: false, findings }; + const transactions = await inspectTransactions(paths, { + name: options.name, + repair, + recoveryStrategy: options.recoveryStrategy + }); + findings.push(...transactions.findings); + findings.push(...await inspectSchemaMigrations(paths, { name: options.name, repair })); + findings.push( + ...await inspectWorkspaceIdentityMigrations(paths, { name: options.name, repair }) + ); + findings.push(...await inspectTrajectoryTailRepairs(paths, { name: options.name, repair })); + findings.push( + ...await inspectTransitionJournals(paths, { + name: options.name, + repair, + recoveryStrategy: options.recoveryStrategy + }) + ); + findings.push(...await inspectCheckpointJournals(paths, { name: options.name, repair })); + findings.push( + ...await inspectNativeEvidenceRetention({ + paths, + name: options.name, + repair, + now: options.now, + deferAll: relocationRecoveryPending || transactions.unfinished.length > 0 + }) + ); + findings.push(...await inspectLocks(paths, repair, transactions.unfinished)); + findings.push(...await inspectSelection(paths, repair)); + findings.push(...await inspectChanges(paths, options.name)); + return { + healthy: findings.every((finding) => finding.severity === "info"), + findings + }; +} + +// domains/comet-native/native-check-receipt.ts +import { createHash as createHash15 } from "node:crypto"; +import { constants as fsConstants6, promises as fs29 } from "node:fs"; +import path42 from "node:path"; +import { TextDecoder as TextDecoder5 } from "node:util"; +var ISSUE_KIND_RANK = { + "conflict-marker": 0, + "trailing-whitespace": 1, + "space-before-tab": 2, + "scope-mismatch": 3, + "unsafe-file": 4, + "scan-limit": 5 +}; +var ScopedFileError = class extends Error { + constructor(kind, message) { + super(message); + this.kind = kind; + } + kind; +}; +function projectionManifest2(projection) { + return { + schema: "comet.native.content-snapshot.v1", + origin: projection.origin, + ...projection.capture ? { capture: projection.capture } : {}, + createdAt: "1970-01-01T00:00:00.000Z", + complete: projection.complete, + limits: projection.limits, + ...projection.policy ? { policy: projection.policy } : {}, + entries: projection.entries, + omitted: projection.omitted, + omittedCount: projection.omittedCount, + ...projection.omissionOverflow ? { omissionOverflow: projection.omissionOverflow } : {} + }; +} +async function collectBoundFacts(options) { + const baseline = projectionManifest2(options.scope.baseline); + const [contract, snapshot2] = await Promise.all([ + collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }), + createNativeCurrentContentSnapshot(options.paths, baseline, { origin: "explicit" }) + ]); + const currentBundle = buildNativeImplementationScopeBundle({ + baseline, + current: snapshot2, + contractHash: options.scope.authority.contractHash, + declaredArtifacts: options.scope.authority.declaredArtifacts, + noCodeReason: options.scope.authority.noCodeReason, + ...options.scope.authority.gitChangedPaths ? { gitChangedPaths: options.scope.authority.gitChangedPaths } : {} + }); + return { + contractHash: contract.contract.contractHash, + snapshotHash: currentBundle.scope.currentProjectionHash + }; +} +function receiptTime(clock, label) { + const value = (clock ?? (() => /* @__PURE__ */ new Date()))(); + if (!(value instanceof Date) || !Number.isFinite(value.getTime())) { + throw new Error(`Native check ${label} time is invalid`); + } + return value.toISOString(); +} +function staleReasons(before, after, scope) { + const reasons = []; + const expectedContractHash = scope.scope.contractHash; + const expectedSnapshotHash = scope.scope.currentProjectionHash; + if (before.contractHash !== expectedContractHash) { + reasons.push("contract-before-does-not-match-scope"); + } + if (before.snapshotHash !== expectedSnapshotHash) { + reasons.push("implementation-before-does-not-match-scope"); + } + if (after.contractHash !== before.contractHash) { + reasons.push("contract-changed-during-check"); + } + if (after.snapshotHash !== before.snapshotHash) { + reasons.push("implementation-changed-during-check"); + } + if (after.contractHash === before.contractHash && after.contractHash !== expectedContractHash) { + reasons.push("contract-after-does-not-match-scope"); + } + if (after.snapshotHash === before.snapshotHash && after.snapshotHash !== expectedSnapshotHash) { + reasons.push("implementation-after-does-not-match-scope"); + } + return reasons; +} +function sameDirectoryIdentity7(identity, stat) { + const stableMetadata = identity.birthtimeMs === stat.birthtimeMs && identity.ctimeMs === stat.ctimeMs; + return stableMetadata && sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity8(left, right) { + const stableMetadata = left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.mtimeMs === right.mtimeMs && left.size === right.size; + return stableMetadata && sameFileObject( + { ...left, birthtime: left.birthtimeMs }, + { + ...right, + birthtime: right.birthtimeMs + } + ); +} +async function captureProjectDirectoryChain(projectRoot, directory) { + const lexicalRoot = path42.resolve(projectRoot); + const lexicalDirectory = path42.resolve(directory); + if (!isInsidePath(lexicalRoot, lexicalDirectory)) { + throw new ScopedFileError("unsafe-file", "Scoped file parent escapes the project root"); + } + const chain = []; + let cursor = lexicalRoot; + for (const segment of [ + "", + ...path42.relative(lexicalRoot, lexicalDirectory).split(path42.sep).filter(Boolean) + ]) { + if (segment) cursor = path42.join(cursor, segment); + let stat; + try { + stat = await fs29.lstat(cursor); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file parent no longer exists"); + } + throw error; + } + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new ScopedFileError("unsafe-file", "Scoped file parent is not a real directory"); + } + const realPath = await fs29.realpath(cursor); + if (chain.length > 0 && !isInsidePath(chain[0].realPath, realPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file parent resolves outside the project"); + } + chain.push({ + path: cursor, + realPath, + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs + }); + } + return { physicalRoot: chain[0].realPath, chain }; +} +async function verifyProjectDirectoryChain(chain) { + for (const identity of chain) { + let stat; + try { + stat = await fs29.lstat(identity.path); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("unsafe-file", "Scoped file parent changed while reading"); + } + throw error; + } + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity7(identity, stat) || await fs29.realpath(identity.path) !== identity.realPath) { + throw new ScopedFileError("unsafe-file", "Scoped file parent changed while reading"); + } + } +} +async function readScopedFile(options) { + const lexicalFile = path42.resolve(options.projectRoot, ...options.file.path.split("/")); + if (!isInsidePath(options.projectRoot, lexicalFile)) { + throw new ScopedFileError("unsafe-file", "Scoped file path escapes the project root"); + } + const { physicalRoot, chain } = await captureProjectDirectoryChain( + options.projectRoot, + path42.dirname(lexicalFile) + ); + let before; + try { + before = await fs29.lstat(lexicalFile); + } catch (error) { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file no longer exists"); + } + throw error; + } + if (!before.isFile() || before.isSymbolicLink()) { + throw new ScopedFileError("unsafe-file", "Scoped file is not a real regular file"); + } + if (before.size !== options.file.expected.size) { + throw new ScopedFileError( + "scope-mismatch", + "Scoped file size no longer matches its projection" + ); + } + const beforeRealPath = await fs29.realpath(lexicalFile); + if (!isInsidePath(physicalRoot, beforeRealPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file resolves outside the project root"); + } + const openFlags = process.platform === "win32" ? "r" : fsConstants6.O_RDONLY | fsConstants6.O_NOFOLLOW | fsConstants6.O_NONBLOCK; + const handle = await fs29.open(lexicalFile, openFlags).catch((error) => { + if (error.code === "ENOENT") { + throw new ScopedFileError("scope-mismatch", "Scoped file no longer exists"); + } + if (error.code === "ELOOP" || error.code === "ENXIO") { + throw new ScopedFileError("unsafe-file", "Scoped file became unsafe while opening"); + } + throw error; + }); + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat(), + fs29.lstat(lexicalFile), + fs29.realpath(lexicalFile) + ]); + await verifyProjectDirectoryChain(chain); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity8(before, opened) || !sameFileIdentity8(opened, pathAfterOpen)) { + throw new ScopedFileError("unsafe-file", "Scoped file changed while opening"); + } + const chunks = []; + const digest = createHash15("sha256"); + const buffer = Buffer.allocUnsafe(64 * 1024); + let total = 0; + while (true) { + const remaining = options.file.expected.size + 1 - total; + if (remaining < 1) { + throw new ScopedFileError("scope-mismatch", "Scoped file exceeds its projected size"); + } + const read = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (read.bytesRead === 0) break; + total += read.bytesRead; + if (total > options.file.expected.size) { + throw new ScopedFileError("scope-mismatch", "Scoped file exceeds its projected size"); + } + const chunk = Buffer.from(buffer.subarray(0, read.bytesRead)); + chunks.push(chunk); + digest.update(chunk); + } + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs29.lstat(lexicalFile), + fs29.realpath(lexicalFile) + ]); + await verifyProjectDirectoryChain(chain); + if (total !== options.file.expected.size || digest.digest("hex") !== options.file.expected.hash) { + throw new ScopedFileError( + "scope-mismatch", + "Scoped file content no longer matches its projection" + ); + } + if (!afterHandle.isFile() || !afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !isInsidePath(physicalRoot, afterRealPath) || !sameFileIdentity8(opened, afterHandle) || !sameFileIdentity8(opened, afterPath)) { + throw new ScopedFileError("unsafe-file", "Scoped file changed while reading"); + } + const content = Buffer.concat(chunks, total); + if (content.includes(0)) return { bytes: total, text: null }; + try { + return { bytes: total, text: new TextDecoder5("utf-8", { fatal: true }).decode(content) }; + } catch { + return { bytes: total, text: null }; + } + } finally { + await handle.close(); + } +} +function selectedScopedFiles(scope) { + const current = new Map(scope.current.entries.map((entry2) => [entry2.path, entry2])); + const files = []; + const mismatches = []; + for (const change of scope.scope.changes) { + if (!change.after) continue; + const entry2 = current.get(change.path); + if (!entry2 || entry2.hash !== change.after.hash || entry2.size !== change.after.size || entry2.type !== "file") { + mismatches.push(change.path); + continue; + } + files.push({ path: change.path, expected: change.after }); + } + files.sort((left, right) => left.path.localeCompare(right.path, "en")); + mismatches.sort((left, right) => left.localeCompare(right, "en")); + return { files, mismatches }; +} +function inspectText(pathRef, text, addIssue) { + const lines = text.split("\n"); + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index].endsWith("\r") ? lines[index].slice(0, -1) : lines[index]; + const lineNumber = index + 1; + if (/^(?:<{7}|={7}|>{7})(?: |$)/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "conflict-marker" }); + } + if (/[ \t]+$/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "trailing-whitespace" }); + } + if (/^ +\t/u.test(line)) { + addIssue({ path: pathRef, line: lineNumber, kind: "space-before-tab" }); + } + } +} +async function executeNativeCheckReceipt(options) { + if (!options.state.implementation_scope) { + throw new Error("Native scoped check requires an implementation scope"); + } + const scope = await readNativeImplementationScopeBundle( + options.paths, + options.state.name, + options.state.implementation_scope + ); + const before = await collectBoundFacts({ paths: options.paths, state: options.state, scope }); + const startedAt = receiptTime(options.clock, "start"); + const selected = selectedScopedFiles(scope); + const issues = []; + let issueCount = 0; + const addIssue = (issue) => { + issueCount += 1; + if (issues.length < NATIVE_CHECK_LIMITS.maxIssues) issues.push(issue); + }; + let candidates = selected.files; + const selectedCount = selected.files.length + selected.mismatches.length; + const scopeDetailOverflow2 = scope.scope.unresolvedScopes.some( + (unresolved) => unresolved.kind === "scope-detail-overflow" + ); + if (scopeDetailOverflow2) { + const overflowPath = scope.scope.changes.at(-1)?.path ?? scope.current.entries.at(-1)?.path ?? scope.baseline.entries.at(-1)?.path; + if (!overflowPath) { + throw new Error("Native implementation scope overflow has no bounded diagnostic path"); + } + addIssue({ path: overflowPath, line: 1, kind: "scan-limit" }); + candidates = []; + } else if (selectedCount > NATIVE_CHECK_LIMITS.maxFiles) { + const selectedPaths = [...selected.files.map((file) => file.path), ...selected.mismatches].sort( + (left, right) => left.localeCompare(right, "en") + ); + addIssue({ + path: selectedPaths[NATIVE_CHECK_LIMITS.maxFiles], + line: 1, + kind: "scan-limit" + }); + candidates = []; + } else { + let total = 0; + for (const file of selected.files) { + if (file.expected.size > NATIVE_CHECK_LIMITS.maxFileBytes) { + addIssue({ path: file.path, line: 1, kind: "scan-limit" }); + candidates = []; + break; + } + total += file.expected.size; + if (total > NATIVE_CHECK_LIMITS.maxTotalBytes) { + addIssue({ path: file.path, line: 1, kind: "scan-limit" }); + candidates = []; + break; + } + } + } + for (const mismatch of selected.mismatches) { + addIssue({ path: mismatch, line: 1, kind: "scope-mismatch" }); + } + let filesScanned = 0; + let binaryFilesSkipped = 0; + let bytesScanned = 0; + for (const file of candidates) { + try { + const scanned = await readScopedFile({ projectRoot: options.paths.projectRoot, file }); + bytesScanned += scanned.bytes; + if (scanned.text === null) { + binaryFilesSkipped += 1; + } else { + filesScanned += 1; + inspectText(file.path, scanned.text, addIssue); + } + } catch (error) { + if (!(error instanceof ScopedFileError)) throw error; + addIssue({ path: file.path, line: 1, kind: error.kind }); + } + } + const after = await collectBoundFacts({ paths: options.paths, state: options.state, scope }); + const endedAt = receiptTime(options.clock, "end"); + const reasons = staleReasons(before, after, scope); + issues.sort( + (left, right) => left.path.localeCompare(right.path, "en") || left.line - right.line || ISSUE_KIND_RANK[left.kind] - ISSUE_KIND_RANK[right.kind] + ); + const receipt = buildNativeCheckReceipt({ + change: options.state.name, + sourceRevision: options.state.revision, + status: issueCount === 0 && reasons.length === 0 ? "passed" : "failed", + startedAt, + endedAt, + contract: { + expectedHash: scope.scope.contractHash, + beforeHash: before.contractHash, + afterHash: after.contractHash + }, + implementation: { + scopeHash: scope.scope.scopeHash, + expectedSnapshotHash: scope.scope.currentProjectionHash, + beforeSnapshotHash: before.snapshotHash, + afterSnapshotHash: after.snapshotHash + }, + counts: { + filesSelected: selectedCount, + filesScanned, + binaryFilesSkipped, + bytesScanned, + issueCount, + recordedIssueCount: issues.length + }, + issues, + issuesTruncated: issueCount > issues.length, + stale: reasons.length > 0, + staleReasons: reasons + }); + const ref = await writeNativeCheckReceipt({ + paths: options.paths, + name: options.state.name, + receipt + }); + return { receipt: parseNativeCheckReceipt(receipt), ref }; +} + +// domains/comet-native/native-check.ts +async function checkNativeChange(options) { + return withNativeMutationLock( + options.paths, + `check ${options.name}`, + () => withNativeTransitionLock(options.paths, options.name, `check ${options.name}`, async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + if (state.phase !== "verify") { + throw new Error(`Native check requires Verify, got ${state.phase}`); + } + if (!state.implementation_scope) { + throw new Error("Native check requires an implementation scope"); + } + const executed = await executeNativeCheckReceipt({ + paths: options.paths, + state + }); + return { change: state, receipt: executed.receipt, ref: executed.ref }; + }) + ); +} + +// domains/comet-native/native-progress-checkpoint.ts +import { randomUUID as randomUUID10 } from "crypto"; +function requiredText2(value, label) { + const normalized = redactNativeCredentialText(value).trim(); + if (normalized.length === 0 || normalized.length > 2e3) { + throw new Error(`${label} must be between 1 and 2000 characters`); + } + return normalized; +} +function expectedRevisionValue(value) { + if (value === void 0) return void 0; + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native expected revision must be a positive integer"); + } + return value; +} +async function checkpointNativeChange(options) { + const summary = requiredText2(options.summary, "Checkpoint summary"); + const nextAction = requiredText2(options.nextAction, "Checkpoint next action"); + const expectedRevision = expectedRevisionValue(options.expectedRevision); + return withNativeMutationLock( + options.paths, + `checkpoint ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `checkpoint ${options.name}`, + async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + const manifest = await createNativeCheckpointManifest( + options.paths, + options.name, + options.artifacts ?? [] + ); + const manifestHash = hashNativeCheckpointManifest(manifest); + const inputHash = sha256Text( + JSON.stringify({ + summary, + nextAction, + artifacts: manifest.artifacts + }) + ); + const existing = await readNativeProgressCheckpoint(options.paths, options.name); + if (existing?.inputHash === inputHash && existing.stateRevision === state.revision && existing.phase === state.phase) { + if (expectedRevision !== void 0 && expectedRevision !== existing.previousRevision && expectedRevision !== state.revision) { + throw new NativeChangeRevisionConflictError( + state.name, + expectedRevision, + state.revision + ); + } + return { + change: state, + checkpoint: existing, + idempotent: true, + expectedRevision: expectedRevision ?? existing.previousRevision, + previousRevision: existing.previousRevision, + revision: state.revision, + outcome: "idempotent", + continuation: nativeContinuation({ state }) + }; + } + if (expectedRevision !== void 0 && state.revision !== expectedRevision) { + throw new NativeChangeRevisionConflictError(state.name, expectedRevision, state.revision); + } + const nextState = { ...state, revision: state.revision + 1 }; + const checkpoint = { + schema: "comet.native.progress-checkpoint.v1", + id: options.checkpointId?.() ?? randomUUID10(), + change: state.name, + phase: state.phase, + previousRevision: state.revision, + stateRevision: nextState.revision, + summary, + nextAction, + inputHash, + manifestHash, + manifestRef: nativeCheckpointManifestRef(manifestHash), + artifactCount: manifest.artifacts.length, + createdAt: (options.now ?? /* @__PURE__ */ new Date()).toISOString() + }; + const journal = await prepareNativeCheckpointJournal({ + paths: options.paths, + previousState: state, + nextState, + checkpoint, + manifest, + now: options.now, + checkpointId: () => checkpoint.id + }); + await options.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeCheckpointLocked( + options.paths, + options.name, + options.hooks + ); + if (!persisted) throw new Error("Native checkpoint journal disappeared before completion"); + return { + change: persisted.nextState, + checkpoint: persisted.checkpoint, + idempotent: false, + expectedRevision: expectedRevision ?? persisted.checkpoint.previousRevision, + previousRevision: persisted.checkpoint.previousRevision, + revision: persisted.nextState.revision, + outcome: "recorded", + continuation: nativeContinuation({ state: persisted.nextState }) + }; + } + ) + ); +} + +// domains/comet-native/native-specs.ts +import { promises as fs30 } from "fs"; +import path43 from "path"; +var MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES = NATIVE_CONTRACT_FILE_LIMITS.maxSpecs * 4; +async function optionalHash3(file) { + try { + return await sha256File(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } +} +async function proposedCapabilities(paths, name) { + const specsDir = path43.join(nativeChangeDir(paths, name), "specs"); + let guard; + try { + guard = await captureNativeProtectedDirectoryGuard({ + root: paths.nativeRoot, + directory: specsDir, + label: "Native proposed specs directory" + }); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + const capabilities = []; + let entryCount = 0; + const directory = await fs30.opendir(specsDir); + try { + for await (const entry2 of directory) { + entryCount += 1; + if (entryCount > MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES) { + throw new Error( + `Native proposed specs directory exceeds ${MAX_NATIVE_PROPOSED_SPEC_DIRECTORY_ENTRIES} entries` + ); + } + if (entry2.isSymbolicLink()) { + throw new Error(`Proposed spec capability must not be a symbolic link: ${entry2.name}`); + } + if (!entry2.isDirectory()) continue; + assertNativeName(entry2.name); + const source = path43.join(specsDir, entry2.name, "spec.md"); + await resolveContainedNativePath(paths.nativeRoot, source); + let stat; + try { + stat = await fs30.lstat(source); + } catch (error) { + if (error.code === "ENOENT") continue; + throw error; + } + if (stat.isSymbolicLink() || !stat.isFile()) { + throw new Error(`Proposed spec must be a regular file: ${entry2.name}`); + } + capabilities.push(entry2.name); + if (capabilities.length > NATIVE_CONTRACT_FILE_LIMITS.maxSpecs) { + throw new Error("Native proposed specs exceed the spec-count budget"); + } + } + } finally { + await directory.close().catch((error) => { + if (error.code !== "ERR_DIR_CLOSED") throw error; + }); + } + await guard.verify(); + return capabilities.sort(); +} +async function reconcileNativeSpecChanges(paths, state) { + const previous = new Map(state.spec_changes.map((change) => [change.capability, change])); + const proposed = await proposedCapabilities(paths, state.name); + const changes = []; + for (const capability of proposed) { + const existing = previous.get(capability); + if (existing?.operation === "remove") { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + if (existing) { + changes.push({ + ...existing, + source: `specs/${capability}/spec.md` + }); + continue; + } + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + changes.push({ + capability, + operation: baseHash === null ? "create" : "replace", + source: `specs/${capability}/spec.md`, + base_hash: baseHash + }); + } + for (const change of state.spec_changes) { + if (change.operation === "remove" && !proposed.includes(change.capability)) { + changes.push(change); + } + } + return changes.sort((left, right) => left.capability.localeCompare(right.capability)); +} +async function refreshNativeSpecChanges(paths, state) { + const proposed = await proposedCapabilities(paths, state.name); + const changes = []; + for (const capability of proposed) { + const existing = state.spec_changes.find((change) => change.capability === capability); + if (existing?.operation === "remove") { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + changes.push({ + capability, + operation: baseHash === null ? "create" : "replace", + source: `specs/${capability}/spec.md`, + base_hash: baseHash + }); + } + for (const change of state.spec_changes) { + if (change.operation !== "remove" || proposed.includes(change.capability)) continue; + const canonical = canonicalSpecPath(paths, change.capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + if (baseHash !== null) { + changes.push({ ...change, base_hash: baseHash }); + } + } + return changes.sort((left, right) => left.capability.localeCompare(right.capability)); +} +async function rebaseNativeSpecChanges(options) { + assertNativeName(options.name); + const summary = redactNativeCredentialText(options.summary); + assertNativeTrajectoryText(summary, "Spec rebase summary"); + return withNativeMutationLock( + options.paths, + `rebase specs for ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `rebase specs for ${options.name}`, + async () => { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + if (state.phase === "shape") { + throw new Error("Shape spec metadata is refreshed by the next command"); + } + if (state.archived) throw new Error(`Native change ${state.name} is already archived`); + const changeDir = nativeChangeDir(options.paths, options.name); + const run = await readNativeRunState(changeDir); + if (!run || run.runId !== state.run_id || run.currentStep !== state.phase || run.pending) { + throw new Error(`Native Run state is missing or inconsistent for ${state.name}`); + } + const specChanges = await refreshNativeSpecChanges(options.paths, state); + const nextState = { + ...state, + revision: state.revision + 1, + phase: "build", + spec_changes: specChanges, + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + }; + const nextRun = { + ...run, + currentStep: "build", + iteration: run.iteration + 1, + pending: null, + status: "running" + }; + const evidenceHash = sha256Text( + JSON.stringify({ + operation: "spec-rebase", + change: state.name, + summary, + specChanges + }) + ); + await prepareNativeTransition({ + paths: options.paths, + previousState: state, + nextState, + previousRun: run, + nextRun, + evidenceHash, + eventData: { + previousPhase: state.phase, + nextPhase: "build", + evidenceHash, + summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }, + operation: "spec-rebase", + now: options.now, + transitionId: options.transitionId + }); + const rebased = await continueNativeTransitionLocked(options.paths, options.name); + if (!rebased) throw new Error("Native spec rebase journal disappeared before completion"); + return rebased; + } + ) + ); +} +async function markNativeSpecRemoval(paths, name, capability) { + assertNativeName(name); + assertNativeName(capability); + return withNativeMutationLock( + paths, + `remove spec ${capability} from ${name}`, + () => withNativeTransitionLock(paths, name, `remove spec ${capability} from ${name}`, async () => { + await settleNativeChangeJournalsLocked(paths, name); + return markNativeSpecRemovalLocked(paths, name, capability); + }) + ); +} +async function markNativeSpecRemovalLocked(paths, name, capability) { + const state = await readNativeChange(paths, name); + if (state.phase === "archive" || state.archived) { + throw new Error(`Native change ${name} no longer accepts spec changes`); + } + const proposed = await proposedCapabilities(paths, name); + if (proposed.includes(capability)) { + throw new Error(`Capability ${capability} has both a proposed spec and a remove intent`); + } + const previous = state.spec_changes.find((change) => change.capability === capability); + if (previous?.operation === "remove") return state; + const canonical = canonicalSpecPath(paths, capability); + await resolveContainedNativePath(paths.nativeRoot, canonical); + const baseHash = await optionalHash3(canonical); + if (baseHash === null) throw new Error(`Canonical spec is missing: ${capability}`); + const updated = { + ...state, + spec_changes: [ + ...state.spec_changes.filter((change) => change.capability !== capability), + { capability, operation: "remove", base_hash: baseHash } + ].sort((left, right) => left.capability.localeCompare(right.capability)) + }; + await compareAndSwapNativeChangeLocked(paths, updated, state.revision); + return updated; +} +async function readNativeProposedSpecs(paths, name) { + const changeDir = nativeChangeDir(paths, name); + const result2 = {}; + let totalBytes = 0; + for (const capability of await proposedCapabilities(paths, name)) { + const source = await readNativeBoundedTextFile({ + root: changeDir, + ref: `specs/${capability}/spec.md`, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + totalBytes += source.size; + if (totalBytes > NATIVE_CONTRACT_FILE_LIMITS.maxTotalBytes) { + throw new Error("Native proposed specs exceed the total byte budget"); + } + result2[capability] = source.text; + } + return result2; +} + +// domains/comet-native/native-transitions.ts +import { randomUUID as randomUUID11 } from "crypto"; + +// domains/comet-native/native-guards.ts +import { promises as fs31 } from "fs"; +import path44 from "path"; +function validation(findings) { + return { valid: findings.length === 0, findings }; +} +async function validateBuildArtifacts(paths, evidence) { + const findings = []; + if ((evidence.noCodeReason ?? "").trim().length > 0) return findings; + if (!evidence.artifacts || evidence.artifacts.length === 0) { + return [ + { + code: "build-evidence-missing", + message: "Build requires an artifact reference or an explicit no-code reason" + } + ]; + } + for (const artifact of evidence.artifacts) { + if (path44.isAbsolute(artifact) || artifact.split(/[\\/]/u).includes("..") || /^(?:[A-Za-z]:|~|[\\/])/u.test(artifact)) { + findings.push({ + code: "build-artifact-unsafe", + message: `Unsafe build artifact: ${artifact}` + }); + continue; + } + const target = path44.resolve(paths.projectRoot, ...artifact.split(/[\\/]/u)); + if (!isInsidePath(paths.projectRoot, target)) { + findings.push({ + code: "build-artifact-unsafe", + message: `Unsafe build artifact: ${artifact}` + }); + continue; + } + try { + await fs31.access(target); + } catch { + findings.push({ + code: "build-artifact-missing", + message: `Build artifact does not exist: ${artifact}`, + path: artifact + }); + } + } + return findings; +} +async function inspectNativeGuard(options) { + const findings = []; + const changeDir = nativeChangeDir(options.paths, options.state.name); + if (options.evidence.summary.trim().length === 0) { + findings.push({ + code: "transition-summary-missing", + message: "Phase transition requires a summary" + }); + } + if (options.evidence.confirmed && options.state.phase !== "shape" && options.state.phase !== "build") { + findings.push({ + code: "confirmation-not-shape", + message: "Explicit confirmation is only valid while leaving Shape or Build" + }); + } + findings.push(...await inspectNativeRunConsistency(options.paths, options.state)); + if (options.state.phase === "shape" || options.state.phase === "build") { + const capturedBaseline = await readNativeBaselineManifest(options.paths, options.state.name); + if (capturedBaseline === null) { + findings.push({ + code: "baseline-snapshot-missing", + message: "Native baseline is missing; restore a trusted baseline before advancing" + }); + } else { + const baseline = await filterNativeContentSnapshotToProjectScope( + options.paths, + capturedBaseline + ); + if (!baseline.complete) { + findings.push({ + code: "baseline-snapshot-incomplete", + message: `Native baseline is incomplete within the project-owned scope (${baseline.omittedCount} omitted entries); resolve the omissions before advancing` + }); + } + } + } + if (options.state.phase === "shape") { + const brief = await validateNativeBrief(changeDir, options.state.brief); + const specs = await validateNativeSpecChanges(options.paths, options.state); + findings.push(...brief.findings, ...specs.findings); + if (findings.length === 0 && !options.evidence.confirmed) { + findings.push({ + code: "shape-confirmation-required", + message: "Native clarification requires explicit user confirmation of the shared understanding before Build" + }); + } + } else if (options.state.phase === "build") { + findings.push( + ...(await validateNativeBrief(changeDir, options.state.brief)).findings, + ...(await validateNativeSpecChanges(options.paths, options.state)).findings + ); + findings.push(...await validateBuildArtifacts(options.paths, options.evidence)); + if (findings.length === 0 && options.state.approval !== "confirmed" && !options.evidence.confirmed) { + findings.push({ + code: "approval-confirmation-required", + message: "Native approval is implicit; confirm the current shared understanding before leaving Build" + }); + } + } else if (options.state.phase === "verify") { + const report = options.evidence.verificationReport ?? options.state.verification_report; + if (!report) { + findings.push({ + code: "verification-report-missing", + message: "Verify requires a report path" + }); + } else { + findings.push(...(await validateNativeVerification(changeDir, report)).findings); + } + if (!options.evidence.verificationResult) { + findings.push({ + code: "verification-result-missing", + message: "Verify requires pass or fail" + }); + } + findings.push(...(await validateNativeSpecChanges(options.paths, options.state)).findings); + } else { + findings.push({ + code: "archive-command-required", + message: "Use comet native archive for the Archive phase" + }); + } + return validation(findings); +} + +// domains/comet-native/native-build-evidence.ts +import { promises as fs32 } from "node:fs"; +import path45 from "node:path"; +var NATIVE_BUILD_EVIDENCE_LIMITS = { + maxDeclaredArtifacts: 128, + maxArtifactPathBytes: 512 +}; +function assertStableNativeSelection(snapshot2, source) { + if (snapshot2.omitted.some((omission) => omission.reason === "git-selection-changed")) { + throw new Error( + `Native Git selection changed while capturing the ${source}; stabilize the Git index and retry Build evidence` + ); + } + if (snapshot2.omitted.some( + (omission) => omission.reason === "physical-enumeration-limit" || omission.reason === "physical-selection-changed" + )) { + throw new Error( + `Native physical selection was incomplete or changed while capturing the ${source}; retry Build evidence with a stable bounded project tree` + ); + } +} +function nativeBaselineIncompleteError(change, baseline) { + const samplePaths = baseline.omitted.slice(0, 20).map((omission) => omission.path); + const omittedByReason = baseline.omitted.reduce((counts, item) => { + counts[item.reason] = (counts[item.reason] ?? 0) + 1; + return counts; + }, {}); + const overflowCount = baseline.omissionOverflow?.count ?? 0; + if (overflowCount > 0) omittedByReason.overflow = overflowCount; + return new NativeBaselineIncompleteError( + change, + baseline.omittedCount, + omittedByReason, + samplePaths, + baseline.omitted.length > samplePaths.length || overflowCount > 0 + ); +} +function nativeBaselineProjectionIncompleteError(change, baseline, projection) { + const retained = new Set(projection.entries.map((entry2) => entry2.path)); + const removedPaths = baseline.entries.filter((entry2) => !retained.has(entry2.path)).slice(0, 20).map((entry2) => entry2.path); + return new NativeBaselineIncompleteError( + change, + projection.omittedCount, + { "manifest-size": projection.omittedCount }, + removedPaths, + projection.omittedCount > removedPaths.length + ); +} +function normalizeProjectRef(value, label) { + const normalized = value.replaceAll("\\", "/").trim(); + if (normalized.length === 0 || normalized !== value.replaceAll("\\", "/") || path45.posix.isAbsolute(normalized) || /^(?:[A-Za-z]:|~)/u.test(normalized) || normalized.split("/").includes("..") || path45.posix.normalize(normalized) !== normalized || normalized === "." || normalized.endsWith("/") || Buffer.byteLength(normalized, "utf8") > NATIVE_BUILD_EVIDENCE_LIMITS.maxArtifactPathBytes || Array.from(normalized).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + })) { + throw new Error(`${label} must be a normalized project-relative path`); + } + return normalized; +} +function baselineArtifactKind(baseline, artifactRef) { + if (baseline.entries.some((entry2) => entry2.path === artifactRef)) return "file"; + if (baseline.entries.some((entry2) => entry2.path.startsWith(`${artifactRef}/`))) { + return "directory"; + } + return null; +} +async function inspectDeclaredArtifact(paths, baseline, rawRef) { + const artifactRef = normalizeProjectRef(rawRef, "Native build artifact"); + const sensitiveReason = nativeSensitiveArtifactReason(paths, artifactRef); + if (sensitiveReason) { + throw new Error(`Native build artifact is excluded as ${sensitiveReason}: ${artifactRef}`); + } + const target = path45.resolve(paths.projectRoot, ...artifactRef.split("/")); + if (!isInsidePath(paths.projectRoot, target)) { + throw new Error(`Native build artifact escapes the project: ${artifactRef}`); + } + await resolveContainedNativePath(paths.projectRoot, target); + try { + const stat = await fs32.lstat(target); + if (stat.isSymbolicLink()) { + throw new Error(`Native build artifact must not be a symlink or junction: ${artifactRef}`); + } + const realTarget = await fs32.realpath(target); + const realProjectRoot = await fs32.realpath(paths.projectRoot); + if (!isInsidePath(realProjectRoot, realTarget)) { + throw new Error(`Native build artifact resolves outside the project: ${artifactRef}`); + } + if (stat.isFile()) return { path: artifactRef, kind: "file" }; + if (stat.isDirectory()) return { path: artifactRef, kind: "directory" }; + throw new Error(`Native build artifact is not a file or directory: ${artifactRef}`); + } catch (error) { + if (error.code !== "ENOENT") throw error; + const kind = baselineArtifactKind(baseline, artifactRef); + if (kind === null) { + throw new Error(`Native build artifact does not exist: ${artifactRef}`, { cause: error }); + } + return { path: artifactRef, kind }; + } +} +async function collectDeclaredArtifacts(options) { + if (options.refs.length > NATIVE_BUILD_EVIDENCE_LIMITS.maxDeclaredArtifacts) { + throw new Error("Native build evidence exceeds its declared-artifact budget"); + } + const artifacts = await Promise.all( + options.refs.map( + (reference) => inspectDeclaredArtifact(options.paths, options.baseline, reference) + ) + ); + artifacts.sort( + (left, right) => left.path.localeCompare(right.path) || left.kind.localeCompare(right.kind) + ); + if (new Set(artifacts.map((artifact) => artifact.path)).size !== artifacts.length) { + throw new Error("Native build evidence contains duplicate or conflicting artifacts"); + } + return artifacts; +} +function partialFindings(unresolvedScopes) { + return unresolvedScopes.map((scope) => ({ + code: "verification-scope-partial", + message: `${scope.id}: ${scope.reason}`, + ...scope.path === null ? {} : { path: scope.path } + })); +} +function partialScopeHash(value) { + if (!/^[a-f0-9]{64}$/u.test(value)) { + throw new Error("Native partial allowance requires a scope hash"); + } + return value; +} +async function inspectNativeBuildEvidence(options) { + if (options.state.phase !== "build") { + throw new Error(`Native build evidence requires Build, got ${options.state.phase}`); + } + if ((options.noCodeReason ?? "").trim().length > 0 && options.artifactRefs.length > 0) { + throw new Error("Native build evidence cannot combine artifacts with a no-code reason"); + } + const storedBaseline = await readNativeBaselineManifest(options.paths, options.state.name); + if (storedBaseline === null) throw new Error("Native change has no baseline content snapshot"); + const baseline = await filterNativeContentSnapshotToProjectScope(options.paths, storedBaseline); + assertStableNativeSelection(baseline, "baseline projection"); + if (!baseline.complete) { + throw nativeBaselineIncompleteError(options.state.name, baseline); + } + const contract = await collectNativeContractFiles({ + changeDir: nativeChangeDir(options.paths, options.state.name), + briefRef: options.state.brief, + specChanges: options.state.spec_changes + }); + const declaredArtifacts = await collectDeclaredArtifacts({ + paths: options.paths, + baseline, + refs: options.artifactRefs + }); + const current = await createNativeCurrentContentSnapshot(options.paths, baseline, { + origin: "explicit", + now: options.now + }); + assertStableNativeSelection(current, "current snapshot"); + const bundle = buildNativeImplementationScopeBundle({ + baseline, + current, + contractHash: contract.contract.contractHash, + declaredArtifacts, + noCodeReason: options.noCodeReason ?? null + }); + if (!bundle.baseline.complete) { + throw nativeBaselineProjectionIncompleteError(options.state.name, baseline, bundle.baseline); + } + const scopeRef = nativeEvidenceRef("scopes", bundle.scope.scopeHash); + if (bundle.scope.complete) { + if (options.allowPartialScopeHash !== void 0 && options.allowPartialScopeHash !== null || options.partialReason !== void 0 && options.partialReason !== null) { + throw new Error("Complete Native build evidence must not include a partial allowance"); + } + return { + contract, + bundle, + scopeRef, + allowance: null, + allowanceRef: null, + findings: [], + unresolvedScopes: [] + }; + } + if (options.allowPartialScopeHash === void 0 || options.allowPartialScopeHash === null) { + return { + contract, + bundle, + scopeRef, + allowance: null, + allowanceRef: null, + findings: partialFindings(bundle.scope.unresolvedScopes), + unresolvedScopes: bundle.scope.unresolvedScopes + }; + } + if (!options.confirmed) { + throw new Error("Native partial verification requires explicit confirmation"); + } + const expectedScopeHash = partialScopeHash(options.allowPartialScopeHash); + if (expectedScopeHash !== bundle.scope.scopeHash) { + throw new Error("Native partial allowance does not match the current implementation scope"); + } + const allowance = buildNativePartialAllowance({ + change: options.state.name, + scopeBundle: bundle, + allowedScopeIds: bundle.scope.unresolvedScopes.map((scope) => scope.id), + reason: options.partialReason ?? "", + confirmedSummary: options.confirmedSummary ?? "", + sourceRevision: options.state.revision, + now: options.now + }); + const allowanceRef = nativeEvidenceRef("allowances", allowance.allowanceHash); + return { + contract, + bundle, + scopeRef, + allowance, + allowanceRef, + findings: [], + unresolvedScopes: bundle.scope.unresolvedScopes + }; +} +async function persistNativeBuildEvidence(options) { + const scopeRef = await writeNativeImplementationScope({ + paths: options.paths, + name: options.state.name, + bundle: options.preparation.bundle + }); + if (scopeRef !== options.preparation.scopeRef) { + throw new Error("Native implementation scope persistence ref changed"); + } + if (options.includeAllowance === false || options.preparation.allowance === null) return; + const allowanceRef = await writeNativePartialAllowance({ + paths: options.paths, + name: options.state.name, + allowance: options.preparation.allowance + }); + if (allowanceRef !== options.preparation.allowanceRef) { + throw new Error("Native partial allowance persistence ref changed"); + } +} + +// domains/comet-native/native-transitions.ts +function hasEvidenceRetreatExtras(evidence) { + return evidence.confirmed !== void 0 || evidence.artifacts !== void 0 || evidence.noCodeReason !== void 0 || evidence.allowPartialScopeHash !== void 0 || evidence.partialReason !== void 0 || evidence.verificationResult !== void 0 || evidence.verificationReport !== void 0 || evidence.verificationReceipt !== void 0 || evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0 || evidence.repairOverrideSignature !== void 0 || evidence.repairOverrideSummary !== void 0; +} +function repairFinding(decision) { + if (decision.reasonCode === "override-already-used") { + return { + code: "repair-override-exhausted", + message: `Native repair already used its override for signature: ${decision.signatureHash}` + }; + } + if (decision.disposition === "warn") { + return { + code: "repair-stagnation-warning", + message: `Native repair repeated the same failure signature: ${decision.signatureHash}` + }; + } + if (decision.disposition === "manual-stop") { + return { + code: "repair-stagnation-stop", + message: `Native repair stopped after repeated failure signature: ${decision.signatureHash}` + }; + } + return { + code: "repair-iteration-limit", + message: `Native repair reached its total iteration limit at signature: ${decision.signatureHash}` + }; +} +function validateNativeAdvanceEvidence(evidence) { + assertNativeTrajectoryText(evidence.summary, "Native transition summary"); + if (evidence.noCodeReason !== void 0) { + assertNativeTrajectoryText(evidence.noCodeReason, "Native transition no-code reason"); + } + if (evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0) { + normalizeNativeRepairFailureTokens({ + categories: evidence.repairFailureCategories, + failedCheckIds: evidence.repairFailedCheckIds + }); + } + if (evidence.repairOverrideSignature !== void 0 && !/^[a-f0-9]{64}$/u.test(evidence.repairOverrideSignature)) { + throw new Error("Native repair override signature must be a SHA-256 hash"); + } + if (evidence.repairOverrideSummary !== void 0) { + hashNativeRepairOverrideSummary(evidence.repairOverrideSummary); + } +} +function normalizeNativeAdvanceEvidence(evidence) { + return { + ...evidence, + summary: redactNativeCredentialText(evidence.summary), + ...evidence.noCodeReason === void 0 ? {} : { noCodeReason: redactNativeCredentialText(evidence.noCodeReason) }, + ...evidence.partialReason === void 0 ? {} : { partialReason: redactNativeCredentialText(evidence.partialReason) }, + ...evidence.repairOverrideSummary === void 0 ? {} : { repairOverrideSummary: redactNativeCredentialText(evidence.repairOverrideSummary) } + }; +} +function validateRepairEvidence(state, evidence) { + const hasFailureFacts = evidence.repairFailureCategories !== void 0 || evidence.repairFailedCheckIds !== void 0; + if (hasFailureFacts && (state.phase !== "verify" || evidence.verificationResult !== "fail")) { + throw new Error("Native repair failure facts are only valid for a failed Verify outcome"); + } + const hasOverrideSignature = evidence.repairOverrideSignature !== void 0; + const hasOverrideSummary = evidence.repairOverrideSummary !== void 0; + if (hasOverrideSignature !== hasOverrideSummary) { + throw new Error("Native repair override signature and summary must be provided together"); + } + if ((hasOverrideSignature || hasOverrideSummary) && state.phase !== "build") { + throw new Error("Native repair override is only valid while leaving Build"); + } +} +async function retreatStaleNativeEvidence(options) { + if (hasEvidenceRetreatExtras(options.transition.evidence)) { + throw new Error("Native evidence retreat only accepts a transition summary"); + } + const previousPhase = options.state.phase; + if (previousPhase !== "verify" && previousPhase !== "archive" || options.run.currentStep !== previousPhase || options.run.pending !== null) { + throw new Error("Native Verify/Archive Run cannot retreat evidence safely"); + } + const evidenceIsFresh = previousPhase === "archive" ? ["complete", "partial"].includes( + (await inspectNativeVerificationFreshness({ + paths: options.transition.paths, + state: options.state, + now: options.transition.now + })).freshness + ) : (await inspectNativeImplementationScopeFreshness({ + paths: options.transition.paths, + state: options.state, + now: options.transition.now + })).freshness === "fresh"; + if (evidenceIsFresh) { + const findings = structureNativeFindings({ + paths: options.transition.paths, + state: options.state, + findings: [ + previousPhase === "archive" ? { + code: "archive-command-required", + message: "Current verification evidence is fresh; use Native Archive preview" + } : { + code: "verification-result-missing", + message: "Current implementation scope is fresh; complete Verify with a result and report" + } + ] + }); + return { + change: options.state, + previousPhase, + next: "manual", + nextCommand: previousPhase === "archive" ? `comet native archive ${options.state.name} --dry-run` : null, + findings, + continuation: nativeContinuation({ + state: options.state, + archiveReady: previousPhase === "archive", + clarificationMode: options.transition.clarificationMode + }) + }; + } + const nextState = { + ...options.state, + revision: options.state.revision + 1, + phase: "build", + verification_result: "pending", + verification_report: null, + implementation_scope: null, + verification_evidence: null, + partial_allowance: null + }; + const nextRun = { + ...options.run, + currentStep: "build", + iteration: options.run.iteration + 1, + pending: null, + status: "running" + }; + const eventData = { + previousPhase, + nextPhase: "build", + evidenceHash: options.evidenceHash, + summary: options.transition.evidence.summary, + artifacts: [], + noCodeReason: null, + verificationResult: null + }; + const journal = await prepareNativeTransition({ + paths: options.transition.paths, + previousState: options.state, + nextState, + previousRun: options.run, + nextRun, + evidenceHash: options.evidenceHash, + eventData, + operation: "evidence-retreat", + now: options.transition.now, + transitionId: options.transition.transitionId + }); + await options.transition.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeTransitionLocked( + options.transition.paths, + options.state.name, + options.transition.hooks + ); + if (!persisted) throw new Error("Native evidence retreat journal disappeared before completion"); + return { + change: persisted, + previousPhase, + next: "auto", + nextCommand: null, + findings: [], + continuation: nativeContinuation({ + state: persisted, + clarificationMode: options.transition.clarificationMode + }) + }; +} +async function advanceNativeChange(options) { + const normalizedOptions = { + ...options, + evidence: normalizeNativeAdvanceEvidence(options.evidence) + }; + validateNativeAdvanceEvidence(normalizedOptions.evidence); + return withNativeMutationLock( + options.paths, + `advance ${options.name}`, + () => withNativeTransitionLock( + options.paths, + options.name, + `advance ${options.name}`, + () => advanceNativeChangeLocked(normalizedOptions) + ) + ); +} +async function advanceNativeChangeLocked(options) { + await settleNativeChangeJournalsLocked(options.paths, options.name); + const state = await readNativeChange(options.paths, options.name); + const previousPhase = state.phase; + const changeDir = nativeChangeDir(options.paths, options.name); + const hash6 = nativeAdvanceEvidenceHash(options.evidence); + const existingRun = await readNativeRunState(changeDir); + if (existingRun) { + const trajectory = await readNativeTrajectory(changeDir, existingRun.trajectoryRef); + const last = trajectory.at(-1); + if (last?.type === "state_transitioned" && last.data.evidenceHash === hash6 && last.data.nextPhase === state.phase) { + const repair = Object.hasOwn(last.data, "repairStagnation") ? await inspectLatestNativeRepairDecision(options.paths, state) : null; + const repairFindings2 = repair && repair.disposition !== "continue" ? structureNativeFindings({ + paths: options.paths, + state, + findings: [repairFinding(repair)] + }) : []; + const stopped = repair?.disposition === "manual-stop" || repair?.disposition === "hard-stop"; + return { + change: state, + previousPhase: last.data.previousPhase ?? state.phase, + next: stopped ? "manual" : "auto", + nextCommand: stopped ? null : state.phase === "archive" ? `comet native archive ${state.name} --dry-run` : null, + findings: repairFindings2, + continuation: nativeContinuation({ + state, + findings: repairFindings2, + archiveReady: state.phase === "archive" && state.verification_result === "pass", + clarificationMode: options.clarificationMode + }), + ...repair ? { repair } : {} + }; + } + } + if (state.phase === "archive") { + if (!existingRun) throw new Error("Native Archive Run state is missing"); + return retreatStaleNativeEvidence({ + transition: options, + state, + run: existingRun, + evidenceHash: hash6 + }); + } + if (state.phase === "verify" && !hasEvidenceRetreatExtras(options.evidence)) { + if (!existingRun) throw new Error("Native Verify Run state is missing"); + const freshness = await inspectNativeImplementationScopeFreshness({ + paths: options.paths, + state, + now: options.now + }); + if (freshness.freshness !== "fresh") { + return retreatStaleNativeEvidence({ + transition: options, + state, + run: existingRun, + evidenceHash: hash6 + }); + } + } + const candidate = { + ...state, + spec_changes: await reconcileNativeSpecChanges(options.paths, state) + }; + validateRepairEvidence(state, options.evidence); + const guard = await inspectNativeGuard({ + paths: options.paths, + state: candidate, + evidence: options.evidence, + clarificationMode: options.clarificationMode + }); + if (!guard.valid) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: guard.findings + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + const shapeContract = state.phase === "shape" ? await collectNativeContractFiles({ + changeDir, + briefRef: candidate.brief, + specChanges: candidate.spec_changes + }) : null; + if (state.phase !== "build" && (options.evidence.allowPartialScopeHash !== void 0 || options.evidence.partialReason !== void 0)) { + throw new Error("Native partial scope allowance is only valid while leaving Build"); + } + const buildEvidence = state.phase === "build" ? await inspectNativeBuildEvidence({ + paths: options.paths, + state: candidate, + artifactRefs: options.evidence.artifacts ?? [], + noCodeReason: options.evidence.noCodeReason ?? null, + allowPartialScopeHash: options.evidence.allowPartialScopeHash ?? null, + partialReason: options.evidence.partialReason ?? null, + confirmedSummary: options.evidence.summary, + confirmed: options.evidence.confirmed ?? false, + now: options.now + }) : null; + if (buildEvidence && (state.approved_contract_hash ?? null) !== buildEvidence.contract.contract.contractHash && !options.evidence.confirmed) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: [ + { + code: "contract-changed-after-approval", + message: "Native contract changed after approval; re-confirm the current contract before leaving Build" + } + ] + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + const preparedScope = buildEvidence ? { + scopeHash: buildEvidence.bundle.scope.scopeHash, + scopeRef: buildEvidence.scopeRef, + complete: buildEvidence.bundle.scope.complete, + unresolvedScopeCount: buildEvidence.unresolvedScopes.length, + partialAllowanceRef: buildEvidence.allowanceRef, + acceptancePage: projectNativeAcceptancePage({ + criteria: buildEvidence.contract.contract.acceptance, + acceptanceHash: buildEvidence.contract.contract.acceptanceHash + }) + } : void 0; + if (buildEvidence && buildEvidence.findings.length > 0) { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence, + includeAllowance: false + }); + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: buildEvidence.findings + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }), + preparedScope + }; + } + let repairEventProjection = null; + let repairScopeHashForEvent = buildEvidence ? nativeRepairScopeHash(buildEvidence.bundle) : null; + if (state.phase === "build" && buildEvidence) { + const repairGuard = await inspectNativeRepairBuildGuard({ + paths: options.paths, + state, + currentImplementationScope: buildEvidence.bundle, + ...options.evidence.repairOverrideSignature && options.evidence.repairOverrideSummary ? { + override: { + expectedSignatureHash: options.evidence.repairOverrideSignature, + summary: options.evidence.repairOverrideSummary + } + } : {} + }); + if (repairGuard.disposition !== "proceed") { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence, + includeAllowance: false + }); + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: [ + repairGuard.disposition === "hard-stop" && repairGuard.overrideRecorded ? { + code: "repair-override-exhausted", + message: `Native repair already used its override for signature: ${repairGuard.signatureHash}` + } : repairFinding({ + disposition: repairGuard.disposition, + reasonCode: repairGuard.disposition === "hard-stop" ? "repair-iteration-limit" : "repeated-failure-stop", + signatureHash: repairGuard.signatureHash + }) + ] + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }), + preparedScope: preparedScope ? { ...preparedScope, partialAllowanceRef: null } : preparedScope + }; + } + repairEventProjection = repairGuard.eventProjection; + } + const verificationEvidence = state.phase === "verify" ? await inspectNativeVerificationEvidence({ + paths: options.paths, + state: candidate, + result: options.evidence.verificationResult, + reportRef: options.evidence.verificationReport, + receiptRef: options.evidence.verificationReceipt ?? null, + now: options.now + }) : null; + if (verificationEvidence && !verificationEvidence.ready) { + const findings = structureNativeFindings({ + paths: options.paths, + state, + findings: verificationEvidence.findingCodes.map((code) => ({ + code, + message: `Native verification evidence is not current: ${code}` + })) + }); + return { + change: state, + previousPhase, + next: "manual", + nextCommand: null, + findings, + continuation: nativeContinuation({ + state, + findings, + clarificationMode: options.clarificationMode + }) + }; + } + let repairDecision = null; + if (state.phase === "verify" && options.evidence.verificationResult === "fail" && verificationEvidence?.ready && verificationEvidence.envelope) { + const repairResult = await inspectNativeRepairFailureForTransition({ + paths: options.paths, + state, + envelope: verificationEvidence.envelope, + ...options.evidence.repairFailureCategories ? { categories: options.evidence.repairFailureCategories } : {}, + ...options.evidence.repairFailedCheckIds ? { failedCheckIds: options.evidence.repairFailedCheckIds } : {} + }); + repairEventProjection = repairResult.eventProjection; + repairScopeHashForEvent = repairResult.facts.implementationScopeHash; + repairDecision = projectNativeRepairDecision(repairResult); + } + let run = existingRun; + if (!run) { + if (state.run_id !== null || state.phase !== "shape") { + throw new Error("Native Run state is missing or inconsistent"); + } + run = startNativeRun( + NATIVE_RUNTIME_PACKAGE, + options.runId?.() ?? randomUUID11(), + NATIVE_RUNTIME_HASH + ); + } + if (run.currentStep !== state.phase) { + throw new Error(`Native Run step ${run.currentStep ?? "(none)"} does not match ${state.phase}`); + } + const decision = decideWithResolver( + NATIVE_RUNTIME_PACKAGE, + run, + /* @__PURE__ */ new Set(), + nativePhaseResolver, + void 0 + ); + if (!decision.action) throw new Error(decision.reason ?? "Native runtime produced no action"); + const advanced = recordOutcomeWithResolver( + NATIVE_RUNTIME_PACKAGE, + decision.state, + { + actionId: decision.action.id, + status: "succeeded", + summary: options.evidence.summary, + state: options.evidence.verificationResult ? { verification_result: options.evidence.verificationResult } : void 0 + }, + nativePhaseResolver, + void 0 + ); + if (!advanced.currentStep) throw new Error("Archive completion must use the archive command"); + const updated = { + ...candidate, + revision: state.revision + 1, + phase: advanced.currentStep, + approval: options.evidence.confirmed ? "confirmed" : state.approval, + approved_contract_hash: state.phase === "shape" ? shapeContract.contract.contractHash : state.phase === "build" && options.evidence.confirmed ? buildEvidence.contract.contract.contractHash : state.approved_contract_hash ?? null, + run_id: run.runId, + ...state.phase === "build" ? { + verification_result: "pending", + verification_report: null, + implementation_scope: buildEvidence.scopeRef, + partial_allowance: buildEvidence.allowanceRef, + verification_evidence: null + } : {}, + ...state.phase === "verify" ? { + verification_result: options.evidence.verificationResult, + verification_report: verificationEvidence.envelope.reportRef, + verification_evidence: verificationEvidence.evidenceRef + } : {} + }; + const eventData = { + previousPhase, + nextPhase: updated.phase, + evidenceHash: hash6, + summary: options.evidence.summary, + artifacts: options.evidence.artifacts ?? [], + noCodeReason: options.evidence.noCodeReason ?? null, + verificationResult: options.evidence.verificationResult ?? null, + ...(state.phase === "build" || state.phase === "verify") && (state.phase === "build" ? buildEvidence.bundle.scope.scopeHash : verificationEvidence.envelope.implementationScopeHash) ? { + implementationScopeHash: state.phase === "build" ? buildEvidence.bundle.scope.scopeHash : verificationEvidence.envelope.implementationScopeHash + } : {}, + ...repairScopeHashForEvent ? { repairScopeHash: repairScopeHashForEvent } : {}, + ...repairEventProjection ? { repairStagnation: repairEventProjection } : {} + }; + if (state.phase === "build" && buildEvidence) { + await persistNativeBuildEvidence({ + paths: options.paths, + state, + preparation: buildEvidence + }); + } + if (state.phase === "verify" && verificationEvidence) { + await persistNativeVerificationEvidence({ + paths: options.paths, + state, + preparation: verificationEvidence + }); + } + const journal = await prepareNativeTransition({ + paths: options.paths, + previousState: state, + nextState: updated, + previousRun: existingRun, + nextRun: advanced, + evidenceHash: hash6, + eventData, + now: options.now, + transitionId: options.transitionId + }); + await options.hooks?.afterPrepared?.(journal); + const persisted = await continueNativeTransitionLocked( + options.paths, + options.name, + options.hooks + ); + if (!persisted) throw new Error("Native transition journal disappeared before completion"); + const repairFindings = repairDecision && repairDecision.disposition !== "continue" ? structureNativeFindings({ + paths: options.paths, + state: persisted, + findings: [repairFinding(repairDecision)] + }) : []; + const repairStopped = repairDecision?.disposition === "manual-stop" || repairDecision?.disposition === "hard-stop"; + return { + change: persisted, + previousPhase, + next: repairStopped ? "manual" : "auto", + nextCommand: repairStopped ? null : persisted.phase === "archive" ? `comet native archive ${persisted.name} --dry-run` : null, + findings: repairFindings, + continuation: nativeContinuation({ + state: persisted, + findings: repairFindings, + archiveReady: persisted.phase === "archive" && persisted.verification_result === "pass", + clarificationMode: options.clarificationMode + }), + ...preparedScope ? { preparedScope } : {}, + ...repairDecision ? { repair: repairDecision } : {} + }; +} + +// domains/comet-native/native-hook-guard.ts +import { promises as fs33 } from "fs"; +import path46 from "path"; + +// domains/comet-entry/hook-adapter.ts +import { readFileSync } from "fs"; +var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "applypatch", + "create", + "createfile", + "deletefile", + "edit", + "editfile", + "patch", + "strreplaceeditor", + "write", + "writefile", + "writefiletool" +]); +var NON_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "glob", + "grep", + "listfiles", + "read", + "readfile", + "search", + "view" +]); +var SINGULAR_PATH_KEYS = ["file_path", "filePath", "path", "target_file", "targetFile"]; +var PLURAL_PATH_KEYS = ["file_paths", "filePaths", "paths", "files", "targets"]; +var NESTED_TARGET_KEYS = ["operations", "edits"]; +var PATCH_KEYS = ["patch", "diff", "patchText", "patch_text", "changes"]; +function isRecord2(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function normalizedToolName(value) { + return value.toLowerCase().replace(/[^a-z0-9]+/gu, ""); +} +function readToolName(input) { + for (const key of ["tool_name", "toolName", "tool", "name"]) { + const value = input[key]; + if (typeof value === "string" && value.trim()) return value.trim(); + } + return null; +} +function parseJsonValue(value) { + if (typeof value !== "string") return value; + const source = value.trim(); + if (!source.startsWith("{") && !source.startsWith("[")) return value; + try { + return JSON.parse(source); + } catch { + return value; + } +} +function readToolArguments(input) { + for (const key of ["tool_input", "toolInput", "toolArgs", "tool_args", "arguments"]) { + if (input[key] !== void 0) return parseJsonValue(input[key]); + } + return input; +} +function patchTargets(source) { + const targets = []; + const patterns = [ + /^\*\*\* (?:Add|Update|Delete) File:\s+(.+?)\s*$/gmu, + /^\+\+\+\s+(?:b\/)?(.+?)\s*$/gmu + ]; + for (const pattern of patterns) { + for (const match of source.matchAll(pattern)) { + const target = match[1]?.trim(); + if (target && target !== "/dev/null") targets.push(target); + } + } + return targets; +} +function addTarget(targets, value) { + if (typeof value === "string") { + const target = value.trim(); + if (target) targets.push(target); + return; + } + if (Array.isArray(value)) { + for (const entry2 of value) addTarget(targets, entry2); + return; + } + if (!isRecord2(value)) return; + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, value[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, value[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, value[key]); +} +function collectTargets(input, args) { + const targets = []; + const records = [args, input].filter(isRecord2); + for (const record8 of records) { + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, record8[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, record8[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, record8[key]); + for (const key of PATCH_KEYS) { + const value = record8[key]; + if (typeof value === "string") targets.push(...patchTargets(value)); + } + } + if (typeof args === "string") targets.push(...patchTargets(args)); + return [...new Set(targets)]; +} +function parseCometHookRequest(source, filePath) { + if (filePath?.trim()) { + return { intent: "write", targets: [filePath.trim()], toolName: null }; + } + if (!source.trim()) return { intent: "unknown", targets: [], toolName: null }; + let input; + try { + input = JSON.parse(source); + } catch { + return { intent: "unknown", targets: [], toolName: null }; + } + if (!isRecord2(input)) return { intent: "unknown", targets: [], toolName: null }; + const toolName = readToolName(input); + const targets = collectTargets(input, readToolArguments(input)); + if (toolName && WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName + }; + } + if (toolName && NON_WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { intent: "non-write", targets: [], toolName }; + } + if (toolName) return { intent: "unknown", targets, toolName }; + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName: null + }; +} +function readCometHookRequest() { + const filePath = process.env.FILE_PATH; + if (filePath?.trim()) return parseCometHookRequest("", filePath); + if (process.stdin.isTTY) return parseCometHookRequest("", filePath); + try { + return parseCometHookRequest(readFileSync(0, "utf8"), filePath); + } catch { + return parseCometHookRequest("", filePath); + } +} + +// domains/comet-native/native-hook-guard.ts +function isWithin(parent, target) { + const relative = path46.relative(parent, target); + return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative); +} +function requestTargetsAreControlOnly(projectRoot, nativeRoot, request) { + return request.targets.length > 0 && request.targets.every((targetPath) => { + const target = path46.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) return true; + const relative = path46.relative(projectRoot, target).replaceAll("\\", "/"); + return relative === ".comet/config.yaml" || isWithin(nativeRoot, target); + }); +} +async function activeNativeContext(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (!config || !(config.workflows ?? [config.default_workflow]).includes("native")) return null; + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + let entries; + try { + entries = await fs33.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return { paths, changes: [] }; + throw error; + } + const changes = []; + for (const entry2 of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue; + const state = await readNativeChange(paths, entry2.name); + if (!state.archived) changes.push(state); + } + return { paths, changes }; +} +async function readNativeHookRequest() { + const { intent, targets } = await readCometHookRequest(); + return { intent, targets }; +} +async function inspectNativeHookGuard(projectRoot, request, selectedChangeName) { + const context = await activeNativeContext(projectRoot); + if (!context) return { allowed: true, reason: "Native workflow is not enabled" }; + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (context.changes.length === 0) { + return { + allowed: true, + reason: requestTargetsAreControlOnly(projectRoot, context.paths.nativeRoot, request) ? "Native control artifact write" : "No Native changes exist" + }; + } + let change; + if (selectedChangeName) { + change = context.changes.find((candidate) => candidate.name === selectedChangeName); + if (!change) { + return { + allowed: false, + reason: `Selected Native change ${selectedChangeName} is missing or archived; resume /comet-native before retrying`, + workflow: "native", + change: selectedChangeName + }; + } + } else if (context.changes.length === 1) { + change = context.changes[0]; + } else { + const selectedName2 = await resolveSelectedNativeChange(context.paths); + change = context.changes.find((candidate) => candidate.name === selectedName2); + if (!change) { + return { + allowed: false, + reason: "Multiple Native changes are active; select the change to resume before writing code", + workflow: "native" + }; + } + } + if (change.phase === "build") { + return { + allowed: true, + reason: "Native change is in Build", + workflow: "native", + phase: change.phase, + change: change.name + }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + return { + allowed: false, + reason: `Hook write target could not be determined while Native change ${change.name} is in ${change.phase}; resume /comet-native before retrying`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + let controlTarget = false; + let externalTarget = false; + for (const targetPath of request.targets) { + const target = path46.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) { + externalTarget = true; + continue; + } + const relative = path46.relative(projectRoot, target).replaceAll("\\", "/"); + if (relative === ".comet/config.yaml" || isWithin(context.paths.nativeRoot, target)) { + controlTarget = true; + continue; + } + return { + allowed: false, + reason: `Native change ${change.name} is in ${change.phase}; implementation writes are only allowed in build. Resume /comet-native to continue safely`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + return { + allowed: true, + reason: controlTarget ? "Native control artifact write" : externalTarget ? "Write target is outside the guarded project" : "No guarded write target was provided", + workflow: "native", + phase: change.phase, + change: change.name + }; +} + +// domains/comet-native/native-cli.ts +var NATIVE_SHOW_MAX_SERIALIZED_BYTES = 10 * 1024 * 1024; +var NativeUsageError = class extends Error { +}; +var USAGE = `Usage: comet native [options] + +Commands: + hook-guard [--hook-output copilot] + init [--root ] [--language en|zh-CN] + root show + root move + new [--language en|zh-CN] + spec remove + spec rebase --summary + list [--cursor ] + show + status [] [--cursor ] [--details [--acceptance-cursor ]] + select + checkpoint --summary --next-action [--artifact ] [--expect-revision ] + check + evidence format [--entries ] + next --summary [--confirmed] [--artifact ] [--no-code-reason ] [--allow-partial-scope --partial-reason ] [--result pass|fail] [--report ] [--receipt ] [--failure-category ] [--failed-check ] [--override-repair --override-summary ] + archive --dry-run + archive --expect-preflight + doctor [] [--repair] [--strategy continue|rollback] +`; +function takeFlag(args, name) { + const indexes = args.flatMap((value, index) => value === name ? [index] : []); + if (indexes.length > 1) throw new NativeUsageError(`${name} may only be provided once`); + if (indexes.length === 0) return false; + args.splice(indexes[0], 1); + return true; +} +function takeOption(args, name) { + const indexes = args.flatMap((value2, index2) => value2 === name ? [index2] : []); + if (indexes.length > 1) throw new NativeUsageError(`${name} may only be provided once`); + if (indexes.length === 0) return void 0; + const index = indexes[0]; + const value = args[index + 1]; + if (value === void 0 || value.startsWith("--")) { + throw new NativeUsageError(`${name} requires a value`); + } + args.splice(index, 2); + return value; +} +function takeMany(args, name) { + const values = []; + for (let index = 0; index < args.length; ) { + if (args[index] !== name) { + index += 1; + continue; + } + const value = args[index + 1]; + if (value === void 0 || value.startsWith("--")) { + throw new NativeUsageError(`${name} requires a value`); + } + values.push(value); + args.splice(index, 2); + } + return values; +} +function assertNoArguments(args) { + if (args.length > 0) throw new NativeUsageError(`Unexpected argument: ${args[0]}`); +} +function requiredPositional(args, label) { + const value = args.shift(); + if (!value || value.startsWith("--")) throw new NativeUsageError(`${label} is required`); + return value; +} +function languageOption(args, fallback = "en") { + const language = takeOption(args, "--language") ?? fallback; + if (language !== "en" && language !== "zh-CN") { + throw new NativeUsageError("--language must be en or zh-CN"); + } + return language; +} +function revisionOption(args) { + const value = takeOption(args, "--expect-revision"); + if (value === void 0) return void 0; + if (!/^[1-9]\d*$/u.test(value) || !Number.isSafeInteger(Number(value))) { + throw new NativeUsageError("--expect-revision must be a positive integer"); + } + return Number(value); +} +async function projectRootFrom(explicit) { + return explicit ? path47.resolve(explicit) : discoverNativeProject(process.cwd()); +} +async function configuredPaths(projectRoot) { + const resolved = await resolveNativeProject({ + startPath: projectRoot, + allowMissingConfig: false + }); + return { config: resolved.config, paths: resolved.paths }; +} +async function doctorPaths(projectRoot) { + const config = await readProjectConfig(projectRoot); + return nativeProjectPaths(projectRoot, config?.native.artifact_root ?? "docs"); +} +function success(command, data, text) { + return { command, exitCode: 0, data, text: text ?? JSON.stringify(data, null, 2) + "\n" }; +} +async function readBoundedEvidenceFile(filePath, maxBytes) { + try { + const { bytes } = await readFileRaceSafe(filePath, maxBytes, { + label: "Acceptance evidence entries file" + }); + return bytes.toString("utf8"); + } catch (error) { + if (error instanceof RaceSafeReadError) { + if (error.reason === "not-regular-file") { + throw new Error(`Acceptance evidence entries path is not a regular file: ${filePath}`, { + cause: error + }); + } + if (error.reason === "too-large") { + throw new Error(`Acceptance evidence entries file exceeds ${maxBytes} bytes: ${filePath}`, { + cause: error + }); + } + throw new Error(`Acceptance evidence entries file changed while reading: ${filePath}`, { + cause: error + }); + } + throw error; + } +} +async function readBoundedEvidenceStdin(maxBytes) { + const chunks = []; + let total = 0; + for await (const chunk of process.stdin) { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)); + total += buffer.byteLength; + if (total > maxBytes) { + throw new Error(`Acceptance evidence entries on stdin exceed ${maxBytes} bytes`); + } + chunks.push(buffer); + } + return Buffer.concat(chunks).toString("utf8"); +} +async function dispatch(rawArgs, explicitProjectRoot) { + if (rawArgs.length === 0 || rawArgs[0] === "--help" || rawArgs[0] === "help") { + return { command: rawArgs[0] ?? null, exitCode: 0, data: { usage: USAGE }, text: USAGE }; + } + const command = rawArgs.shift(); + const projectRoot = await projectRootFrom(explicitProjectRoot); + if (command === "hook-guard") { + const hookOutput = takeOption(rawArgs, "--hook-output"); + if (hookOutput !== void 0 && hookOutput !== "copilot") { + throw new NativeUsageError("--hook-output must be copilot"); + } + assertNoArguments(rawArgs); + const result2 = await inspectNativeHookGuard(projectRoot, await readNativeHookRequest()); + if (hookOutput === "copilot") { + return { + command, + exitCode: 0, + data: result2, + text: result2.allowed ? "{}\n" : `${JSON.stringify({ + permissionDecision: "deny", + permissionDecisionReason: result2.reason + })} +` + }; + } + return result2.allowed ? { command, exitCode: 0, data: result2 } : { + command, + exitCode: 2, + data: result2, + error: { code: "blocked", message: result2.reason } + }; + } + if (command === "init") { + const requestedRoot = takeOption(rawArgs, "--root"); + const existing = await readProjectConfig(projectRoot); + const language = languageOption(rawArgs, existing?.native.language ?? "en"); + assertNoArguments(rawArgs); + if (existing?.native.pending_root_move) { + throw new Error(`Native root move ${existing.native.pending_root_move.id} is incomplete`); + } + const artifactRoot = normalizeArtifactRootRef( + requestedRoot ?? existing?.native.artifact_root ?? "docs" + ); + if (existing && requestedRoot && existing.native.artifact_root !== artifactRoot) { + throw new Error( + `Configured Native artifact root is ${existing.native.artifact_root}; refusing conflicting root ${artifactRoot}` + ); + } + const config = existing ? { ...existing, native: { ...existing.native, language } } : defaultProjectConfig(artifactRoot, language); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + await ensureNativeDirectories(paths); + await writeProjectConfig(projectRoot, config); + return success( + "init", + { + projectRoot, + artifactRoot: config.native.artifact_root, + nativeRoot: paths.nativeRoot, + language + }, + `Initialized Comet Native at ${paths.nativeRoot} +` + ); + } + if (command === "root") { + const subcommand = requiredPositional(rawArgs, "root subcommand"); + if (subcommand === "show") { + assertNoArguments(rawArgs); + const config = await readProjectConfig(projectRoot); + if (!config) throw new Error(".comet/config.yaml was not found"); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + return success("root show", { + projectRoot, + artifactRoot: config.native.artifact_root, + language: config.native.language, + nativeRoot: paths.nativeRoot, + pendingRootMove: config.native.pending_root_move ?? null + }); + } + if (subcommand === "move") { + const target = requiredPositional(rawArgs, "artifact root"); + assertNoArguments(rawArgs); + const result2 = await moveNativeRoot({ projectRoot, toArtifactRoot: target }); + return success("root move", result2, `Moved Comet Native to ${result2.toNativeRoot} +`); + } + throw new NativeUsageError(`Unknown root command: ${subcommand}`); + } + if (command === "new") { + const name = requiredPositional(rawArgs, "change name"); + let config = await readProjectConfig(projectRoot); + const language = languageOption(rawArgs, config?.native.language ?? "en"); + assertNoArguments(rawArgs); + const shouldWriteConfig = config === null; + if (!config) { + config = defaultProjectConfig("docs", language); + } + if (config.native.pending_root_move) { + throw new Error(`Native root move ${config.native.pending_root_move.id} is incomplete`); + } + if (shouldWriteConfig) await writeProjectConfig(projectRoot, config); + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + await ensureNativeDirectories(paths); + const state = await createNativeChange({ paths, name, language }); + await selectNativeChange(paths, state.name); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "new", + { ...state, continuation: status.continuation }, + `Created Native change ${state.name} +` + ); + } + if (command === "spec") { + const subcommand = requiredPositional(rawArgs, "spec subcommand"); + if (subcommand === "remove") { + const name = requiredPositional(rawArgs, "change name"); + const capability = requiredPositional(rawArgs, "capability"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const state = await markNativeSpecRemoval(paths, name, capability); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "spec remove", + { ...state, continuation: status.continuation }, + `Marked Native capability ${capability} for removal in ${name} +` + ); + } + if (subcommand === "rebase") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const state = await rebaseNativeSpecChanges({ paths, name, summary }); + const status = await inspectNativeStatus(paths, state.name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "spec rebase", + { ...state, continuation: status.continuation }, + `Rebased Native specs for ${name} +` + ); + } + throw new NativeUsageError(`Unknown spec command: ${subcommand}`); + } + if (command === "list") { + const cursor = takeOption(rawArgs, "--cursor"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const page = await listNativeStatusPage(paths, { + ...cursor ? { cursor } : {}, + clarificationMode: config.native.clarification_mode + }); + return success("list", page); + } + if (command === "show") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + return success("show", { + name, + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + migrationRequired: true, + message: inspection.message + }); + } + if (inspection.status !== "current" || !inspection.state) { + throw new NativeRuntimeCompatibilityError( + inspection.schema, + inspection.minimumRuntimeVersion + ); + } + const state = inspection.state; + const changeDir = nativeChangeDir(paths, name); + const proposedSpecs = await readNativeProposedSpecs(paths, name); + const brief = await readNativeBoundedTextFile({ + root: changeDir, + ref: state.brief, + maxBytes: NATIVE_CONTRACT_FILE_LIMITS.maxFileBytes + }); + const payload = { + state, + brief: brief.text, + proposedSpecs + }; + if (Buffer.byteLength(JSON.stringify(payload), "utf8") > NATIVE_SHOW_MAX_SERIALIZED_BYTES) { + throw new Error("Native show output exceeds its serialized byte budget"); + } + return success("show", payload); + } + if (command === "status") { + const details = takeFlag(rawArgs, "--details"); + const cursor = takeOption(rawArgs, "--cursor"); + const acceptanceCursor2 = takeOption(rawArgs, "--acceptance-cursor"); + const name = rawArgs[0]?.startsWith("--") ? void 0 : rawArgs.shift(); + if (details && !name) throw new NativeUsageError("status --details requires a change name"); + if (cursor && name) throw new NativeUsageError("--cursor is only valid for status lists"); + if (cursor && details) throw new NativeUsageError("--cursor cannot be combined with --details"); + if (acceptanceCursor2 && !details) { + throw new NativeUsageError("--acceptance-cursor requires status --details"); + } + if (acceptanceCursor2 && !name) { + throw new NativeUsageError("--acceptance-cursor requires a change name"); + } + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const data = name ? await inspectNativeStatus(paths, name, { + details, + ...acceptanceCursor2 ? { acceptanceCursor: acceptanceCursor2 } : {}, + clarificationMode: config.native.clarification_mode + }) : await listNativeStatusPage(paths, { + ...cursor ? { cursor } : {}, + clarificationMode: config.native.clarification_mode + }); + return success("status", data); + } + if (command === "select") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + await selectNativeChange(paths, name); + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + return success( + "select", + { selected: name, continuation: status.continuation }, + `Selected Native change ${name} +` + ); + } + if (command === "checkpoint") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + const nextAction = takeOption(rawArgs, "--next-action"); + if (!nextAction) throw new NativeUsageError("--next-action is required"); + const artifacts = takeMany(rawArgs, "--artifact"); + const expectedRevision = revisionOption(rawArgs); + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const result2 = await checkpointNativeChange({ + paths, + name, + summary, + nextAction, + artifacts, + expectedRevision + }); + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + const manifestRef = path47.relative( + paths.projectRoot, + path47.join(nativeChangeDir(paths, name), ...result2.checkpoint.manifestRef.split("/")) + ).replaceAll("\\", "/"); + return success("checkpoint", { + ...result2, + checkpoint: { ...result2.checkpoint, manifestRef }, + continuation: status.continuation + }); + } + if (command === "check") { + const name = requiredPositional(rawArgs, "change name"); + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + const checked = await checkNativeChange({ paths, name }); + const data = { + ref: checked.ref, + hash: checked.receipt.receiptHash, + status: checked.receipt.status, + checker: checked.receipt.checker, + counts: checked.receipt.counts, + issues: checked.receipt.issues, + issuesTruncated: checked.receipt.issuesTruncated, + stale: checked.receipt.stale, + staleReasons: checked.receipt.staleReasons, + startedAt: checked.receipt.startedAt, + endedAt: checked.receipt.endedAt, + sourceRevision: checked.receipt.sourceRevision + }; + const passed = checked.receipt.status === "passed" && !checked.receipt.stale; + return { + command: "check", + exitCode: passed ? 0 : 1, + data, + text: `Native check ${passed ? "passed" : "failed"}: ${checked.ref} +` + }; + } + if (command === "evidence") { + const subcommand = requiredPositional(rawArgs, "evidence subcommand"); + if (subcommand === "format") { + const entriesPath = takeOption(rawArgs, "--entries"); + assertNoArguments(rawArgs); + let raw; + if (entriesPath) { + raw = await readBoundedEvidenceFile( + path47.resolve(entriesPath), + MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES + ); + } else { + if (process.stdin.isTTY) { + throw new NativeUsageError( + "evidence format requires acceptance evidence entries JSON on stdin, or --entries " + ); + } + raw = await readBoundedEvidenceStdin(MAX_NATIVE_IMPLEMENTATION_EVIDENCE_DOCUMENT_BYTES); + } + let entries; + try { + entries = JSON.parse(raw); + } catch (error) { + throw new Error( + `Acceptance evidence entries must be valid JSON: ${error.message}`, + { cause: error } + ); + } + if (!Array.isArray(entries)) { + throw new Error("Acceptance evidence entries must be a JSON array"); + } + const block = serializeNativeVerificationMachineBlock(entries); + return success("evidence format", { block }, `${block} +`); + } + throw new NativeUsageError(`Unknown evidence command: ${subcommand}`); + } + if (command === "next") { + const name = requiredPositional(rawArgs, "change name"); + const summary = takeOption(rawArgs, "--summary"); + if (!summary) throw new NativeUsageError("--summary is required"); + const confirmed = takeFlag(rawArgs, "--confirmed"); + const artifacts = takeMany(rawArgs, "--artifact"); + const noCodeReason = takeOption(rawArgs, "--no-code-reason"); + const allowPartialScopeHash = takeOption(rawArgs, "--allow-partial-scope"); + const partialReason = takeOption(rawArgs, "--partial-reason"); + const verificationResult = takeOption(rawArgs, "--result"); + const verificationReport = takeOption(rawArgs, "--report"); + const verificationReceipt = takeOption(rawArgs, "--receipt"); + const repairFailureCategories = takeMany(rawArgs, "--failure-category"); + const repairFailedCheckIds = takeMany(rawArgs, "--failed-check"); + const repairOverrideSignature = takeOption(rawArgs, "--override-repair"); + const repairOverrideSummary = takeOption(rawArgs, "--override-summary"); + if (verificationResult !== void 0 && verificationResult !== "pass" && verificationResult !== "fail") { + throw new NativeUsageError("--result must be pass or fail"); + } + if (allowPartialScopeHash === void 0 !== (partialReason === void 0)) { + throw new NativeUsageError( + "--allow-partial-scope and --partial-reason must be provided together" + ); + } + if (allowPartialScopeHash && !/^[a-f0-9]{64}$/u.test(allowPartialScopeHash)) { + throw new NativeUsageError("--allow-partial-scope must be a SHA-256 hash"); + } + if (allowPartialScopeHash && !confirmed) { + throw new NativeUsageError("--allow-partial-scope requires --confirmed"); + } + if ((repairFailureCategories.length > 0 || repairFailedCheckIds.length > 0) && verificationResult !== "fail") { + throw new NativeUsageError("--failure-category and --failed-check require --result fail"); + } + if (verificationReceipt && verificationResult === void 0) { + throw new NativeUsageError("--receipt requires --result"); + } + if (repairOverrideSignature === void 0 !== (repairOverrideSummary === void 0)) { + throw new NativeUsageError( + "--override-repair and --override-summary must be provided together" + ); + } + if (repairOverrideSignature && !/^[a-f0-9]{64}$/u.test(repairOverrideSignature)) { + throw new NativeUsageError("--override-repair must be a SHA-256 hash"); + } + if (repairOverrideSignature && verificationResult !== void 0) { + throw new NativeUsageError("--override-repair cannot be combined with --result"); + } + assertNoArguments(rawArgs); + const { config, paths } = await configuredPaths(projectRoot); + const evidence = { + summary, + ...confirmed ? { confirmed: true } : {}, + ...artifacts.length > 0 ? { artifacts } : {}, + ...noCodeReason ? { noCodeReason } : {}, + ...allowPartialScopeHash ? { allowPartialScopeHash } : {}, + ...partialReason ? { partialReason } : {}, + ...verificationResult ? { verificationResult } : {}, + ...verificationReport ? { verificationReport } : {}, + ...verificationReceipt ? { verificationReceipt } : {}, + ...repairFailureCategories.length > 0 ? { repairFailureCategories } : {}, + ...repairFailedCheckIds.length > 0 ? { repairFailedCheckIds } : {}, + ...repairOverrideSignature ? { repairOverrideSignature } : {}, + ...repairOverrideSummary ? { repairOverrideSummary } : {} + }; + const result2 = await advanceNativeChange({ + paths, + name, + evidence, + clarificationMode: config.native.clarification_mode + }); + if (result2.next === "manual") { + const repairBlocked = result2.repair?.disposition === "manual-stop" || result2.repair?.disposition === "hard-stop" || result2.findings.some( + (finding) => [ + "repair-stagnation-stop", + "repair-iteration-limit", + "repair-override-exhausted" + ].includes(finding.code) + ); + return { + command: "next", + exitCode: repairBlocked ? 75 : 65, + data: result2, + error: { + code: repairBlocked ? "blocked" : "invalid-data", + message: result2.findings[0]?.message ?? "Native phase guard failed" + } + }; + } + const status = await inspectNativeStatus(paths, name, { + clarificationMode: config.native.clarification_mode + }); + return success("next", { ...result2, continuation: status.continuation }); + } + if (command === "archive") { + const name = requiredPositional(rawArgs, "change name"); + const dryRun = takeFlag(rawArgs, "--dry-run"); + const expectedPreflightHash = takeOption(rawArgs, "--expect-preflight"); + if (dryRun && expectedPreflightHash) { + throw new NativeUsageError("--dry-run and --expect-preflight cannot be combined"); + } + if (!dryRun && !expectedPreflightHash) { + throw new NativeUsageError("archive requires --dry-run or --expect-preflight "); + } + if (expectedPreflightHash && !/^[a-f0-9]{64}$/u.test(expectedPreflightHash)) { + throw new NativeUsageError("--expect-preflight must be a SHA-256 hash"); + } + assertNoArguments(rawArgs); + const { paths } = await configuredPaths(projectRoot); + if (dryRun) { + const preview = await inspectNativeArchivePreflight({ paths, name }); + return success( + "archive --dry-run", + preview, + `Native Archive preview ${preview.preflightHash}: ${preview.ready ? "ready" : "blocked"} +` + ); + } + const state = await readNativeChange(paths, name); + const result2 = await archiveNativeChange({ + paths, + name, + expectedPreflightHash + }); + return success( + "archive", + { ...result2, continuation: nativeContinuation({ state, done: true }) }, + `Archived Native change ${name} to ${result2.archiveDir} +` + ); + } + if (command === "doctor") { + const repair = takeFlag(rawArgs, "--repair"); + const recoveryStrategy = takeOption(rawArgs, "--strategy"); + if (recoveryStrategy !== void 0 && recoveryStrategy !== "continue" && recoveryStrategy !== "rollback") { + throw new NativeUsageError("--strategy must be continue or rollback"); + } + const name = rawArgs[0]?.startsWith("--") ? void 0 : rawArgs.shift(); + assertNoArguments(rawArgs); + const paths = await doctorPaths(projectRoot); + const result2 = await doctorNativeProject({ + paths, + ...name ? { name } : {}, + repair, + ...recoveryStrategy ? { recoveryStrategy } : {} + }); + return result2.healthy ? success("doctor", result2) : { + command: "doctor", + exitCode: 65, + data: result2, + error: { code: "invalid-data", message: "Native project needs attention" } + }; + } + throw new NativeUsageError(`Unknown Native command: ${command}`); +} +function errorResult(command, error) { + if (error instanceof NativeUsageError) { + return { + command, + exitCode: 64, + error: { code: "usage", message: error.message } + }; + } + if (error instanceof NativeSpecConflictError) { + return { + command, + exitCode: 73, + data: { + capability: error.capability, + expectedHash: error.expectedHash, + actualHash: error.actualHash, + canonicalPath: error.canonicalPath + }, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeArchivePreflightError) { + return { + command, + exitCode: 73, + data: error.preflight, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeChangeRevisionConflictError) { + return { + command, + exitCode: 73, + data: { + change: error.change, + expectedRevision: error.expectedRevision, + actualRevision: error.actualRevision, + outcome: "revision-conflict" + }, + error: { code: "conflict", message: error.message } + }; + } + if (error instanceof NativeBaselineIncompleteError) { + return { + command, + exitCode: 65, + data: { + change: error.change, + complete: false, + omittedCount: error.omittedCount, + omittedByReason: error.omittedByReason, + samplePaths: error.samplePaths, + sampleTruncated: error.sampleTruncated, + effectiveLimits: error.effectiveLimits, + policyHash: error.policyHash, + configPath: ".comet/config.yaml", + supportedFixes: [ + "increase native.snapshot.max_total_bytes or native.snapshot.max_duration_ms", + "add an explicit native.snapshot.exclude pattern for data outside implementation scope" + ], + requiredAction: "resolve-native-baseline" + }, + error: { code: "baseline-incomplete", message: error.message } + }; + } + if (error instanceof Error) { + const systemCode = error.code; + if (systemCode && (/* @__PURE__ */ new Set(["EACCES", "EPERM", "EIO", "EMFILE", "ENFILE", "ENOSPC", "EROFS"])).has(systemCode)) { + return { + command, + exitCode: 70, + error: { code: "internal", message: error.message } + }; + } + const conflict = /\b(lock|transaction|conflict|occupied|incomplete|recovery)\b/iu.test( + error.message + ); + return { + command, + exitCode: conflict ? 73 : 65, + error: { code: conflict ? "conflict" : "invalid-data", message: error.message } + }; + } + return { + command, + exitCode: 70, + error: { code: "internal", message: String(error) } + }; +} +function render(result2, json) { + if (json) { + return { + exitCode: result2.exitCode, + stdout: JSON.stringify({ + command: result2.command, + exitCode: result2.exitCode, + ...result2.data === void 0 ? {} : { data: result2.data }, + ...result2.error === void 0 ? {} : { error: result2.error } + }) + "\n" + }; + } + if (result2.error) { + return { exitCode: result2.exitCode, stderr: result2.error.message }; + } + return { exitCode: result2.exitCode, stdout: result2.text }; +} +async function runNativeCli(argv) { + const args = [...argv]; + const json = args.includes("--json"); + let explicitProjectRoot; + let command = args[0] ?? null; + try { + takeFlag(args, "--json"); + explicitProjectRoot = takeOption(args, "--project-root"); + command = args[0] ?? null; + return render(await dispatch(args, explicitProjectRoot), json); + } catch (error) { + return render(errorResult(command, error), json); + } +} + +// domains/comet-native/native-cli-entry.ts +async function main(argv = process.argv.slice(2)) { + const result2 = await runNativeCli(argv); + if (result2.stdout) process.stdout.write(result2.stdout); + if (result2.stderr) { + process.stderr.write(result2.stderr + (result2.stderr.endsWith("\n") ? "" : "\n")); + } + return result2.exitCode; +} +var entry = process.argv[1]; +if (entry && import.meta.url === pathToFileURL(entry).href) { + void main().then((exitCode) => { + process.exitCode = exitCode; + }); +} +export { + main +}; diff --git a/.pi/skills/comet/SKILL.md b/.pi/skills/comet/SKILL.md new file mode 100644 index 0000000..e0809a1 --- /dev/null +++ b/.pi/skills/comet/SKILL.md @@ -0,0 +1,28 @@ +--- +name: comet +description: "当用户调用 /comet 或需要恢复 active Comet change 时,按项目配置路由到永久的 Comet Native 或 Comet Classic 入口。" +--- + +# Comet 入口 + +`/comet` 只负责选择入口,不包含任何一种工作流的执行方法。 + +1. 先在当前项目尝试 PATH 中安装的 Comet CLI: + + ```text + comet workflow resolve . --json + ``` + +2. **只有**宿主明确报告 `command not found`、`executable not found` 或 `ENOENT`,能够证明 `comet` 不在 PATH 时,才从当前 `SKILL.md` 所在目录定位 ``,运行自带入口 runtime: + + ```text + node /scripts/comet-entry-runtime.mjs . --json + ``` + + CLI 已启动但返回非零、配置解析失败、输出不是 JSON 或字段无效,都不得使用 bundled runtime 重试;停止并原样说明错误,不要回退或猜测。 +3. 解析 JSON。只接受 `schema: comet.workflow-resolution.v1`,且 `skill` 必须是下列两个值之一。 +4. 只按返回的 `skill` 调用一个入口,并把用户原始请求完整交给它: + - `comet-native` → `/comet-native` + - `comet-classic` → `/comet-classic` + +不根据任务大小、文件数量、活跃 change 或模型判断改选另一套工作流。Native 与 Classic 的 change、状态和产物始终彼此独立。 diff --git a/.pi/skills/comet/scripts/comet-entry-runtime.mjs b/.pi/skills/comet/scripts/comet-entry-runtime.mjs new file mode 100644 index 0000000..1775481 --- /dev/null +++ b/.pi/skills/comet/scripts/comet-entry-runtime.mjs @@ -0,0 +1,7946 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path5) { + const ctrl = callVisitor(key, node, visitor, path5); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path5, ctrl); + return visit_(key, ctrl, visitor, path5); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path5 = Object.freeze(path5.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path5); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path5 = Object.freeze(path5.concat(node)); + const ck = visit_("key", node.key, visitor, path5); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path5); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path5) { + const ctrl = await callVisitor(key, node, visitor, path5); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path5, ctrl); + return visitAsync_(key, ctrl, visitor, path5); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path5 = Object.freeze(path5.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path5); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path5 = Object.freeze(path5.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path5); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path5); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path5) { + if (typeof visitor === "function") + return visitor(key, node, path5); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path5); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path5); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path5); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path5); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path5); + return void 0; + } + function replaceNode(key, path5, node) { + const parent = path5[path5.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path5, value) { + let v = value; + for (let i = path5.length - 1; i >= 0; --i) { + const k = path5[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path5) => path5 == null || typeof path5 === "object" && !!path5[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path5, value) { + if (isEmptyPath(path5)) + this.add(value); + else { + const [key, ...rest] = path5; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path5) { + const [key, ...rest] = path5; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path5, keepScalar) { + const [key, ...rest] = path5; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path5) { + const [key, ...rest] = path5; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path5, value) { + const [key, ...rest] = path5; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify2(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify2.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify2.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify2 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify2.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify3 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify3(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify2.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify2.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp.intTime, + timestamp.floatTime, + timestamp.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify2 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify2.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify2.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify2.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path5, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path5, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path5) { + if (Collection.isEmptyPath(path5)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path5) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path5, keepScalar) { + if (Collection.isEmptyPath(path5)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path5, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path5) { + if (Collection.isEmptyPath(path5)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path5) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path5, value) { + if (Collection.isEmptyPath(path5)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path5), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path5, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify2 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path5) => { + let item = cst; + for (const [field, index] of path5) { + const tok = item?.[field]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path5) => { + const parent = visit.itemAtPath(cst, path5.slice(0, -1)); + const field = path5[path5.length - 1][0]; + const coll = parent?.[field]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path5, item, visitor) { + let ctrl = visitor(item, path5); + if (typeof ctrl === "symbol") + return ctrl; + for (const field of ["key", "value"]) { + const token = item[field]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path5.concat([[field, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field === "key") + ctrl = ctrl(item, path5); + } + } + return typeof ctrl === "function" ? ctrl(item, path5) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs4 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs4, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs4); + } else { + Object.assign(it, { key: fs4, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs4 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs4, sep: [] }); + else if (it.sep) + this.stack.push(fs4); + else + Object.assign(it, { key: fs4, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument3(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument3(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify2(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument3; + exports.stringify = stringify2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/comet-entry/entry-runtime.ts +import path4 from "path"; + +// domains/comet-native/native-config.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { promises as fs3 } from "fs"; +import path3 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml = __toESM(require_dist(), 1); + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// domains/comet-native/native-protected-file.ts +import { createHash } from "node:crypto"; +import { constants as fsConstants, promises as fs } from "node:fs"; +import path from "node:path"; +import { TextDecoder } from "node:util"; +function isInside(parent, target) { + const relative = path.relative(parent, target); + return relative === "" || !path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`); +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity(directory, label) { + const stat = await fs.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory, label) { + const lexicalRoot = path.resolve(root); + const lexicalDirectory = path.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path.relative(lexicalRoot, lexicalDirectory).split(path.sep).filter(Boolean)) { + await verifyDirectoryChain(chain, label); + cursor = path.join(cursor, segment); + const identity = await captureDirectoryIdentity(cursor, label); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain(chain, label); + return chain; +} +async function verifyDirectoryChain(chain, label) { + for (const identity of chain) { + const stat = await fs.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit(options.maxBytes); + const file = path.resolve(options.file); + const chain = await captureDirectoryChain(options.root, path.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity(path.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain(chain, options.label); + const before = await fs.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs.realpath(file); + if (!isInside(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + const handle = await fs.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs.lstat(file), + fs.realpath(file) + ]); + await verifyDirectoryChain(chain, options.label); + await verifyDirectoryChain(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity(beforeIdentity, opened) || !sameFileIdentity(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs.lstat(file), + fs.realpath(file) + ]); + await verifyDirectoryChain(chain, options.label); + await verifyDirectoryChain(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity(beforeIdentity, afterHandle) || !sameFileIdentity(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(snapshot.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot, text }; +} + +// domains/comet-native/native-paths.ts +import { promises as fs2 } from "fs"; +import path2 from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs2.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs2.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path2.resolve(startPath); + try { + if (!(await fs2.stat(cursor)).isDirectory()) cursor = path2.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path2.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path2.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path2.join(cursor, ".git"))) return cursor; + const parent = path2.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path2.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized = path2.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized === ".." || normalized.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized === "" ? "." : normalized; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot = record(value, "native.snapshot"); + rejectUnknown(snapshot, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path3.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs3.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml2.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} + +// domains/comet-entry/resolve-entry.ts +function configuredResolution(workflow) { + return { + workflow, + skill: workflow === "native" ? "comet-native" : "comet-classic", + source: "project-config" + }; +} +async function resolveCometEntry(startPath) { + const projectRoot = await discoverNativeProject(startPath); + const config = await readProjectConfig(projectRoot); + if (!config) { + return { + workflow: "classic", + skill: "comet-classic", + source: "legacy-fallback" + }; + } + return configuredResolution(config.default_workflow); +} + +// domains/comet-entry/workflow-resolution.ts +var COMET_WORKFLOW_RESOLUTION_SCHEMA = "comet.workflow-resolution.v1"; +async function resolveCometWorkflowResolution(startPath) { + return { + schema: COMET_WORKFLOW_RESOLUTION_SCHEMA, + ...await resolveCometEntry(startPath) + }; +} +function formatCometWorkflowResolution(resolution) { + return [ + `workflow: ${resolution.workflow}`, + `skill: ${resolution.skill}`, + `source: ${resolution.source}` + ].join("\n"); +} + +// domains/comet-entry/entry-runtime.ts +var USAGE = "Usage: comet-entry-runtime [path] [--json]"; +function parseEntryRuntimeArgs(args) { + let help = false; + let json = false; + let targetPath = null; + for (const arg of args) { + if (arg === "--help" || arg === "-h") { + help = true; + continue; + } + if (arg === "--json") { + json = true; + continue; + } + if (arg.startsWith("-")) { + throw new Error(`Unknown option: ${arg}`); + } + if (targetPath !== null) { + throw new Error(`Unexpected argument: ${arg}`); + } + targetPath = arg; + } + return { help, json, targetPath: path4.resolve(targetPath ?? ".") }; +} +async function runCometEntryRuntime(args, io = { + stdout: (value) => process.stdout.write(value), + stderr: (value) => process.stderr.write(value) +}) { + let parsed; + try { + parsed = parseEntryRuntimeArgs(args); + } catch (error) { + io.stderr(`${error instanceof Error ? error.message : String(error)} +${USAGE} +`); + return 64; + } + if (parsed.help) { + io.stdout(`${USAGE} +`); + return 0; + } + try { + const resolution = await resolveCometWorkflowResolution(parsed.targetPath); + io.stdout( + parsed.json ? `${JSON.stringify(resolution, null, 2)} +` : `${formatCometWorkflowResolution(resolution)} +` + ); + return 0; + } catch (error) { + io.stderr(`${error instanceof Error ? error.message : String(error)} +`); + return 65; + } +} + +// domains/comet-entry/entry-runtime-entry.ts +process.exitCode = await runCometEntryRuntime(process.argv.slice(2)); diff --git a/.pi/skills/comet/scripts/comet-hook-router.mjs b/.pi/skills/comet/scripts/comet-hook-router.mjs new file mode 100644 index 0000000..c1c83b9 --- /dev/null +++ b/.pi/skills/comet/scripts/comet-hook-router.mjs @@ -0,0 +1,10525 @@ +#!/usr/bin/env node +import { createRequire as __cometCreateRequire } from 'module'; +const require = __cometCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __esm = (fn, res, err) => function __init() { + if (err) throw err[0]; + try { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; + } catch (e) { + throw err = [e], e; + } +}; +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js +var require_identity = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/identity.js"(exports) { + "use strict"; + var ALIAS = /* @__PURE__ */ Symbol.for("yaml.alias"); + var DOC = /* @__PURE__ */ Symbol.for("yaml.document"); + var MAP = /* @__PURE__ */ Symbol.for("yaml.map"); + var PAIR = /* @__PURE__ */ Symbol.for("yaml.pair"); + var SCALAR = /* @__PURE__ */ Symbol.for("yaml.scalar"); + var SEQ = /* @__PURE__ */ Symbol.for("yaml.seq"); + var NODE_TYPE = /* @__PURE__ */ Symbol.for("yaml.node.type"); + var isAlias = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === ALIAS; + var isDocument = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === DOC; + var isMap = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === MAP; + var isPair = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === PAIR; + var isScalar = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SCALAR; + var isSeq = (node) => !!node && typeof node === "object" && node[NODE_TYPE] === SEQ; + function isCollection(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true; + } + return false; + } + function isNode(node) { + if (node && typeof node === "object") + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true; + } + return false; + } + var hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor; + exports.ALIAS = ALIAS; + exports.DOC = DOC; + exports.MAP = MAP; + exports.NODE_TYPE = NODE_TYPE; + exports.PAIR = PAIR; + exports.SCALAR = SCALAR; + exports.SEQ = SEQ; + exports.hasAnchor = hasAnchor; + exports.isAlias = isAlias; + exports.isCollection = isCollection; + exports.isDocument = isDocument; + exports.isMap = isMap; + exports.isNode = isNode; + exports.isPair = isPair; + exports.isScalar = isScalar; + exports.isSeq = isSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js +var require_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/visit.js"(exports) { + "use strict"; + var identity = require_identity(); + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove node"); + function visit(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + visit_(null, node, visitor_, Object.freeze([])); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + function visit_(key, node, visitor, path14) { + const ctrl = callVisitor(key, node, visitor, path14); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path14, ctrl); + return visit_(key, ctrl, visitor, path14); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path14 = Object.freeze(path14.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = visit_(i, node.items[i], visitor, path14); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path14 = Object.freeze(path14.concat(node)); + const ck = visit_("key", node.key, visitor, path14); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = visit_("value", node.value, visitor, path14); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + async function visitAsync(node, visitor) { + const visitor_ = initVisitor(visitor); + if (identity.isDocument(node)) { + const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); + if (cd === REMOVE) + node.contents = null; + } else + await visitAsync_(null, node, visitor_, Object.freeze([])); + } + visitAsync.BREAK = BREAK; + visitAsync.SKIP = SKIP; + visitAsync.REMOVE = REMOVE; + async function visitAsync_(key, node, visitor, path14) { + const ctrl = await callVisitor(key, node, visitor, path14); + if (identity.isNode(ctrl) || identity.isPair(ctrl)) { + replaceNode(key, path14, ctrl); + return visitAsync_(key, ctrl, visitor, path14); + } + if (typeof ctrl !== "symbol") { + if (identity.isCollection(node)) { + path14 = Object.freeze(path14.concat(node)); + for (let i = 0; i < node.items.length; ++i) { + const ci = await visitAsync_(i, node.items[i], visitor, path14); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + node.items.splice(i, 1); + i -= 1; + } + } + } else if (identity.isPair(node)) { + path14 = Object.freeze(path14.concat(node)); + const ck = await visitAsync_("key", node.key, visitor, path14); + if (ck === BREAK) + return BREAK; + else if (ck === REMOVE) + node.key = null; + const cv = await visitAsync_("value", node.value, visitor, path14); + if (cv === BREAK) + return BREAK; + else if (cv === REMOVE) + node.value = null; + } + } + return ctrl; + } + function initVisitor(visitor) { + if (typeof visitor === "object" && (visitor.Collection || visitor.Node || visitor.Value)) { + return Object.assign({ + Alias: visitor.Node, + Map: visitor.Node, + Scalar: visitor.Node, + Seq: visitor.Node + }, visitor.Value && { + Map: visitor.Value, + Scalar: visitor.Value, + Seq: visitor.Value + }, visitor.Collection && { + Map: visitor.Collection, + Seq: visitor.Collection + }, visitor); + } + return visitor; + } + function callVisitor(key, node, visitor, path14) { + if (typeof visitor === "function") + return visitor(key, node, path14); + if (identity.isMap(node)) + return visitor.Map?.(key, node, path14); + if (identity.isSeq(node)) + return visitor.Seq?.(key, node, path14); + if (identity.isPair(node)) + return visitor.Pair?.(key, node, path14); + if (identity.isScalar(node)) + return visitor.Scalar?.(key, node, path14); + if (identity.isAlias(node)) + return visitor.Alias?.(key, node, path14); + return void 0; + } + function replaceNode(key, path14, node) { + const parent = path14[path14.length - 1]; + if (identity.isCollection(parent)) { + parent.items[key] = node; + } else if (identity.isPair(parent)) { + if (key === "key") + parent.key = node; + else + parent.value = node; + } else if (identity.isDocument(parent)) { + parent.contents = node; + } else { + const pt = identity.isAlias(parent) ? "alias" : "scalar"; + throw new Error(`Cannot replace node with ${pt} parent`); + } + } + exports.visit = visit; + exports.visitAsync = visitAsync; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js +var require_directives = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/directives.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + var escapeChars = { + "!": "%21", + ",": "%2C", + "[": "%5B", + "]": "%5D", + "{": "%7B", + "}": "%7D" + }; + var escapeTagName = (tn) => tn.replace(/[!,[\]{}]/g, (ch) => escapeChars[ch]); + var Directives = class _Directives { + constructor(yaml, tags) { + this.docStart = null; + this.docEnd = false; + this.yaml = Object.assign({}, _Directives.defaultYaml, yaml); + this.tags = Object.assign({}, _Directives.defaultTags, tags); + } + clone() { + const copy = new _Directives(this.yaml, this.tags); + copy.docStart = this.docStart; + return copy; + } + /** + * During parsing, get a Directives instance for the current document and + * update the stream state according to the current version's spec. + */ + atDocument() { + const res = new _Directives(this.yaml, this.tags); + switch (this.yaml.version) { + case "1.1": + this.atNextDocument = true; + break; + case "1.2": + this.atNextDocument = false; + this.yaml = { + explicit: _Directives.defaultYaml.explicit, + version: "1.2" + }; + this.tags = Object.assign({}, _Directives.defaultTags); + break; + } + return res; + } + /** + * @param onError - May be called even if the action was successful + * @returns `true` on success + */ + add(line, onError) { + if (this.atNextDocument) { + this.yaml = { explicit: _Directives.defaultYaml.explicit, version: "1.1" }; + this.tags = Object.assign({}, _Directives.defaultTags); + this.atNextDocument = false; + } + const parts = line.trim().split(/[ \t]+/); + const name = parts.shift(); + switch (name) { + case "%TAG": { + if (parts.length !== 2) { + onError(0, "%TAG directive should contain exactly two parts"); + if (parts.length < 2) + return false; + } + const [handle, prefix] = parts; + this.tags[handle] = prefix; + return true; + } + case "%YAML": { + this.yaml.explicit = true; + if (parts.length !== 1) { + onError(0, "%YAML directive should contain exactly one part"); + return false; + } + const [version] = parts; + if (version === "1.1" || version === "1.2") { + this.yaml.version = version; + return true; + } else { + const isValid = /^\d+\.\d+$/.test(version); + onError(6, `Unsupported YAML version ${version}`, isValid); + return false; + } + } + default: + onError(0, `Unknown directive ${name}`, true); + return false; + } + } + /** + * Resolves a tag, matching handles to those defined in %TAG directives. + * + * @returns Resolved tag, which may also be the non-specific tag `'!'` or a + * `'!local'` tag, or `null` if unresolvable. + */ + tagName(source, onError) { + if (source === "!") + return "!"; + if (source[0] !== "!") { + onError(`Not a valid tag: ${source}`); + return null; + } + if (source[1] === "<") { + const verbatim = source.slice(2, -1); + if (verbatim === "!" || verbatim === "!!") { + onError(`Verbatim tags aren't resolved, so ${source} is invalid.`); + return null; + } + if (source[source.length - 1] !== ">") + onError("Verbatim tags must end with a >"); + return verbatim; + } + const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s); + if (!suffix) + onError(`The ${source} tag has no suffix`); + const prefix = this.tags[handle]; + if (prefix) { + try { + return prefix + decodeURIComponent(suffix); + } catch (error) { + onError(String(error)); + return null; + } + } + if (handle === "!") + return source; + onError(`Could not resolve tag: ${source}`); + return null; + } + /** + * Given a fully resolved tag, returns its printable string form, + * taking into account current tag prefixes and defaults. + */ + tagString(tag) { + for (const [handle, prefix] of Object.entries(this.tags)) { + if (tag.startsWith(prefix)) + return handle + escapeTagName(tag.substring(prefix.length)); + } + return tag[0] === "!" ? tag : `!<${tag}>`; + } + toString(doc) { + const lines = this.yaml.explicit ? [`%YAML ${this.yaml.version || "1.2"}`] : []; + const tagEntries = Object.entries(this.tags); + let tagNames; + if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) { + const tags = {}; + visit.visit(doc.contents, (_key, node) => { + if (identity.isNode(node) && node.tag) + tags[node.tag] = true; + }); + tagNames = Object.keys(tags); + } else + tagNames = []; + for (const [handle, prefix] of tagEntries) { + if (handle === "!!" && prefix === "tag:yaml.org,2002:") + continue; + if (!doc || tagNames.some((tn) => tn.startsWith(prefix))) + lines.push(`%TAG ${handle} ${prefix}`); + } + return lines.join("\n"); + } + }; + Directives.defaultYaml = { explicit: false, version: "1.2" }; + Directives.defaultTags = { "!!": "tag:yaml.org,2002:" }; + exports.Directives = Directives; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js +var require_anchors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/anchors.js"(exports) { + "use strict"; + var identity = require_identity(); + var visit = require_visit(); + function anchorIsValid(anchor) { + if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { + const sa = JSON.stringify(anchor); + const msg = `Anchor must not contain whitespace or control characters: ${sa}`; + throw new Error(msg); + } + return true; + } + function anchorNames(root) { + const anchors = /* @__PURE__ */ new Set(); + visit.visit(root, { + Value(_key, node) { + if (node.anchor) + anchors.add(node.anchor); + } + }); + return anchors; + } + function findNewAnchor(prefix, exclude) { + for (let i = 1; true; ++i) { + const name = `${prefix}${i}`; + if (!exclude.has(name)) + return name; + } + } + function createNodeAnchors(doc, prefix) { + const aliasObjects = []; + const sourceObjects = /* @__PURE__ */ new Map(); + let prevAnchors = null; + return { + onAnchor: (source) => { + aliasObjects.push(source); + prevAnchors ?? (prevAnchors = anchorNames(doc)); + const anchor = findNewAnchor(prefix, prevAnchors); + prevAnchors.add(anchor); + return anchor; + }, + /** + * With circular references, the source node is only resolved after all + * of its child nodes are. This is why anchors are set only after all of + * the nodes have been created. + */ + setAnchors: () => { + for (const source of aliasObjects) { + const ref = sourceObjects.get(source); + if (typeof ref === "object" && ref.anchor && (identity.isScalar(ref.node) || identity.isCollection(ref.node))) { + ref.node.anchor = ref.anchor; + } else { + const error = new Error("Failed to resolve repeated object (this should not happen)"); + error.source = source; + throw error; + } + } + }, + sourceObjects + }; + } + exports.anchorIsValid = anchorIsValid; + exports.anchorNames = anchorNames; + exports.createNodeAnchors = createNodeAnchors; + exports.findNewAnchor = findNewAnchor; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js +var require_applyReviver = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/applyReviver.js"(exports) { + "use strict"; + function applyReviver(reviver, obj, key, val) { + if (val && typeof val === "object") { + if (Array.isArray(val)) { + for (let i = 0, len = val.length; i < len; ++i) { + const v0 = val[i]; + const v1 = applyReviver(reviver, val, String(i), v0); + if (v1 === void 0) + delete val[i]; + else if (v1 !== v0) + val[i] = v1; + } + } else if (val instanceof Map) { + for (const k of Array.from(val.keys())) { + const v0 = val.get(k); + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + val.delete(k); + else if (v1 !== v0) + val.set(k, v1); + } + } else if (val instanceof Set) { + for (const v0 of Array.from(val)) { + const v1 = applyReviver(reviver, val, v0, v0); + if (v1 === void 0) + val.delete(v0); + else if (v1 !== v0) { + val.delete(v0); + val.add(v1); + } + } + } else { + for (const [k, v0] of Object.entries(val)) { + const v1 = applyReviver(reviver, val, k, v0); + if (v1 === void 0) + delete val[k]; + else if (v1 !== v0) + val[k] = v1; + } + } + } + return reviver.call(obj, key, val); + } + exports.applyReviver = applyReviver; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js +var require_toJS = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/toJS.js"(exports) { + "use strict"; + var identity = require_identity(); + function toJS(value, arg, ctx) { + if (Array.isArray(value)) + return value.map((v, i) => toJS(v, String(i), ctx)); + if (value && typeof value.toJSON === "function") { + if (!ctx || !identity.hasAnchor(value)) + return value.toJSON(arg, ctx); + const data = { aliasCount: 0, count: 1, res: void 0 }; + ctx.anchors.set(value, data); + ctx.onCreate = (res2) => { + data.res = res2; + delete ctx.onCreate; + }; + const res = value.toJSON(arg, ctx); + if (ctx.onCreate) + ctx.onCreate(res); + return res; + } + if (typeof value === "bigint" && !ctx?.keep) + return Number(value); + return value; + } + exports.toJS = toJS; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js +var require_Node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Node.js"(exports) { + "use strict"; + var applyReviver = require_applyReviver(); + var identity = require_identity(); + var toJS = require_toJS(); + var NodeBase = class { + constructor(type) { + Object.defineProperty(this, identity.NODE_TYPE, { value: type }); + } + /** Create a copy of this node. */ + clone() { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** A plain JavaScript representation of this node. */ + toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + if (!identity.isDocument(doc)) + throw new TypeError("A document argument is required"); + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this, "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + }; + exports.NodeBase = NodeBase; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js +var require_Alias = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Alias.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var visit = require_visit(); + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var Alias = class extends Node.NodeBase { + constructor(source) { + super(identity.ALIAS); + this.source = source; + Object.defineProperty(this, "tag", { + set() { + throw new Error("Alias nodes cannot have tags"); + } + }); + } + /** + * Resolve the value of this alias within `doc`, finding the last + * instance of the `source` anchor before this node. + */ + resolve(doc, ctx) { + if (ctx?.maxAliasCount === 0) + throw new ReferenceError("Alias resolution is disabled"); + let nodes; + if (ctx?.aliasResolveCache) { + nodes = ctx.aliasResolveCache; + } else { + nodes = []; + visit.visit(doc, { + Node: (_key, node) => { + if (identity.isAlias(node) || identity.hasAnchor(node)) + nodes.push(node); + } + }); + if (ctx) + ctx.aliasResolveCache = nodes; + } + let found = void 0; + for (const node of nodes) { + if (node === this) + break; + if (node.anchor === this.source) + found = node; + } + return found; + } + toJSON(_arg, ctx) { + if (!ctx) + return { source: this.source }; + const { anchors: anchors2, doc, maxAliasCount } = ctx; + const source = this.resolve(doc, ctx); + if (!source) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new ReferenceError(msg); + } + let data = anchors2.get(source); + if (!data) { + toJS.toJS(source, null, ctx); + data = anchors2.get(source); + } + if (data?.res === void 0) { + const msg = "This should not happen: Alias anchor was not resolved?"; + throw new ReferenceError(msg); + } + if (maxAliasCount >= 0) { + data.count += 1; + if (data.aliasCount === 0) + data.aliasCount = getAliasCount(doc, source, anchors2); + if (data.count * data.aliasCount > maxAliasCount) { + const msg = "Excessive alias count indicates a resource exhaustion attack"; + throw new ReferenceError(msg); + } + } + return data.res; + } + toString(ctx, _onComment, _onChompKeep) { + const src = `*${this.source}`; + if (ctx) { + anchors.anchorIsValid(this.source); + if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) { + const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`; + throw new Error(msg); + } + if (ctx.implicitKey) + return `${src} `; + } + return src; + } + }; + function getAliasCount(doc, node, anchors2) { + if (identity.isAlias(node)) { + const source = node.resolve(doc); + const anchor = anchors2 && source && anchors2.get(source); + return anchor ? anchor.count * anchor.aliasCount : 0; + } else if (identity.isCollection(node)) { + let count = 0; + for (const item of node.items) { + const c = getAliasCount(doc, item, anchors2); + if (c > count) + count = c; + } + return count; + } else if (identity.isPair(node)) { + const kc = getAliasCount(doc, node.key, anchors2); + const vc = getAliasCount(doc, node.value, anchors2); + return Math.max(kc, vc); + } + return 1; + } + exports.Alias = Alias; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js +var require_Scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Node = require_Node(); + var toJS = require_toJS(); + var isScalarValue = (value) => !value || typeof value !== "function" && typeof value !== "object"; + var Scalar = class extends Node.NodeBase { + constructor(value) { + super(identity.SCALAR); + this.value = value; + } + toJSON(arg, ctx) { + return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx); + } + toString() { + return String(this.value); + } + }; + Scalar.BLOCK_FOLDED = "BLOCK_FOLDED"; + Scalar.BLOCK_LITERAL = "BLOCK_LITERAL"; + Scalar.PLAIN = "PLAIN"; + Scalar.QUOTE_DOUBLE = "QUOTE_DOUBLE"; + Scalar.QUOTE_SINGLE = "QUOTE_SINGLE"; + exports.Scalar = Scalar; + exports.isScalarValue = isScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js +var require_createNode = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/createNode.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var defaultTagPrefix = "tag:yaml.org,2002:"; + function findTagObject(value, tagName, tags) { + if (tagName) { + const match = tags.filter((t) => t.tag === tagName); + const tagObj = match.find((t) => !t.format) ?? match[0]; + if (!tagObj) + throw new Error(`Tag ${tagName} not found`); + return tagObj; + } + return tags.find((t) => t.identify?.(value) && !t.format); + } + function createNode(value, tagName, ctx) { + if (identity.isDocument(value)) + value = value.contents; + if (identity.isNode(value)) + return value; + if (identity.isPair(value)) { + const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx); + map.items.push(value); + return map; + } + if (value instanceof String || value instanceof Number || value instanceof Boolean || typeof BigInt !== "undefined" && value instanceof BigInt) { + value = value.valueOf(); + } + const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx; + let ref = void 0; + if (aliasDuplicateObjects && value && typeof value === "object") { + ref = sourceObjects.get(value); + if (ref) { + ref.anchor ?? (ref.anchor = onAnchor(value)); + return new Alias.Alias(ref.anchor); + } else { + ref = { anchor: null, node: null }; + sourceObjects.set(value, ref); + } + } + if (tagName?.startsWith("!!")) + tagName = defaultTagPrefix + tagName.slice(2); + let tagObj = findTagObject(value, tagName, schema.tags); + if (!tagObj) { + if (value && typeof value.toJSON === "function") { + value = value.toJSON(); + } + if (!value || typeof value !== "object") { + const node2 = new Scalar.Scalar(value); + if (ref) + ref.node = node2; + return node2; + } + tagObj = value instanceof Map ? schema[identity.MAP] : Symbol.iterator in Object(value) ? schema[identity.SEQ] : schema[identity.MAP]; + } + if (onTagObj) { + onTagObj(tagObj); + delete ctx.onTagObj; + } + const node = tagObj?.createNode ? tagObj.createNode(ctx.schema, value, ctx) : typeof tagObj?.nodeClass?.from === "function" ? tagObj.nodeClass.from(ctx.schema, value, ctx) : new Scalar.Scalar(value); + if (tagName) + node.tag = tagName; + else if (!tagObj.default) + node.tag = tagObj.tag; + if (ref) + ref.node = node; + return node; + } + exports.createNode = createNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js +var require_Collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Collection.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var identity = require_identity(); + var Node = require_Node(); + function collectionFromPath(schema, path14, value) { + let v = value; + for (let i = path14.length - 1; i >= 0; --i) { + const k = path14[i]; + if (typeof k === "number" && Number.isInteger(k) && k >= 0) { + const a = []; + a[k] = v; + v = a; + } else { + v = /* @__PURE__ */ new Map([[k, v]]); + } + } + return createNode.createNode(v, void 0, { + aliasDuplicateObjects: false, + keepUndefined: false, + onAnchor: () => { + throw new Error("This should not happen, please report a bug."); + }, + schema, + sourceObjects: /* @__PURE__ */ new Map() + }); + } + var isEmptyPath = (path14) => path14 == null || typeof path14 === "object" && !!path14[Symbol.iterator]().next().done; + var Collection = class extends Node.NodeBase { + constructor(type, schema) { + super(type); + Object.defineProperty(this, "schema", { + value: schema, + configurable: true, + enumerable: false, + writable: true + }); + } + /** + * Create a copy of this collection. + * + * @param schema - If defined, overwrites the original's schema + */ + clone(schema) { + const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this)); + if (schema) + copy.schema = schema; + copy.items = copy.items.map((it) => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it); + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** + * Adds a value to the collection. For `!!map` and `!!omap` the value must + * be a Pair instance or a `{ key, value }` object, which may not have a key + * that already exists in the map. + */ + addIn(path14, value) { + if (isEmptyPath(path14)) + this.add(value); + else { + const [key, ...rest] = path14; + const node = this.get(key, true); + if (identity.isCollection(node)) + node.addIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + /** + * Removes a value from the collection. + * @returns `true` if the item was found and removed. + */ + deleteIn(path14) { + const [key, ...rest] = path14; + if (rest.length === 0) + return this.delete(key); + const node = this.get(key, true); + if (identity.isCollection(node)) + return node.deleteIn(rest); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path14, keepScalar) { + const [key, ...rest] = path14; + const node = this.get(key, true); + if (rest.length === 0) + return !keepScalar && identity.isScalar(node) ? node.value : node; + else + return identity.isCollection(node) ? node.getIn(rest, keepScalar) : void 0; + } + hasAllNullValues(allowScalar) { + return this.items.every((node) => { + if (!identity.isPair(node)) + return false; + const n = node.value; + return n == null || allowScalar && identity.isScalar(n) && n.value == null && !n.commentBefore && !n.comment && !n.tag; + }); + } + /** + * Checks if the collection includes a value with the key `key`. + */ + hasIn(path14) { + const [key, ...rest] = path14; + if (rest.length === 0) + return this.has(key); + const node = this.get(key, true); + return identity.isCollection(node) ? node.hasIn(rest) : false; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path14, value) { + const [key, ...rest] = path14; + if (rest.length === 0) { + this.set(key, value); + } else { + const node = this.get(key, true); + if (identity.isCollection(node)) + node.setIn(rest, value); + else if (node === void 0 && this.schema) + this.set(key, collectionFromPath(this.schema, rest, value)); + else + throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`); + } + } + }; + exports.Collection = Collection; + exports.collectionFromPath = collectionFromPath; + exports.isEmptyPath = isEmptyPath; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js +var require_stringifyComment = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) { + "use strict"; + var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#"); + function indentComment(comment, indent) { + if (/^\n+$/.test(comment)) + return comment.substring(1); + return indent ? comment.replace(/^(?! *$)/gm, indent) : comment; + } + var lineComment = (str, indent, comment) => str.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str.endsWith(" ") ? "" : " ") + comment; + exports.indentComment = indentComment; + exports.lineComment = lineComment; + exports.stringifyComment = stringifyComment; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js +var require_foldFlowLines = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/foldFlowLines.js"(exports) { + "use strict"; + var FOLD_FLOW = "flow"; + var FOLD_BLOCK = "block"; + var FOLD_QUOTED = "quoted"; + function foldFlowLines(text, indent, mode = "flow", { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) { + if (!lineWidth || lineWidth < 0) + return text; + if (lineWidth < minContentWidth) + minContentWidth = 0; + const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length); + if (text.length <= endStep) + return text; + const folds = []; + const escapedFolds = {}; + let end = lineWidth - indent.length; + if (typeof indentAtStart === "number") { + if (indentAtStart > lineWidth - Math.max(2, minContentWidth)) + folds.push(0); + else + end = lineWidth - indentAtStart; + } + let split = void 0; + let prev = void 0; + let overflow = false; + let i = -1; + let escStart = -1; + let escEnd = -1; + if (mode === FOLD_BLOCK) { + i = consumeMoreIndentedLines(text, i, indent.length); + if (i !== -1) + end = i + endStep; + } + for (let ch; ch = text[i += 1]; ) { + if (mode === FOLD_QUOTED && ch === "\\") { + escStart = i; + switch (text[i + 1]) { + case "x": + i += 3; + break; + case "u": + i += 5; + break; + case "U": + i += 9; + break; + default: + i += 1; + } + escEnd = i; + } + if (ch === "\n") { + if (mode === FOLD_BLOCK) + i = consumeMoreIndentedLines(text, i, indent.length); + end = i + indent.length + endStep; + split = void 0; + } else { + if (ch === " " && prev && prev !== " " && prev !== "\n" && prev !== " ") { + const next = text[i + 1]; + if (next && next !== " " && next !== "\n" && next !== " ") + split = i; + } + if (i >= end) { + if (split) { + folds.push(split); + end = split + endStep; + split = void 0; + } else if (mode === FOLD_QUOTED) { + while (prev === " " || prev === " ") { + prev = ch; + ch = text[i += 1]; + overflow = true; + } + const j = i > escEnd + 1 ? i - 2 : escStart - 1; + if (escapedFolds[j]) + return text; + folds.push(j); + escapedFolds[j] = true; + end = j + endStep; + split = void 0; + } else { + overflow = true; + } + } + } + prev = ch; + } + if (overflow && onOverflow) + onOverflow(); + if (folds.length === 0) + return text; + if (onFold) + onFold(); + let res = text.slice(0, folds[0]); + for (let i2 = 0; i2 < folds.length; ++i2) { + const fold = folds[i2]; + const end2 = folds[i2 + 1] || text.length; + if (fold === 0) + res = ` +${indent}${text.slice(0, end2)}`; + else { + if (mode === FOLD_QUOTED && escapedFolds[fold]) + res += `${text[fold]}\\`; + res += ` +${indent}${text.slice(fold + 1, end2)}`; + } + } + return res; + } + function consumeMoreIndentedLines(text, i, indent) { + let end = i; + let start = i + 1; + let ch = text[start]; + while (ch === " " || ch === " ") { + if (i < start + indent) { + ch = text[++i]; + } else { + do { + ch = text[++i]; + } while (ch && ch !== "\n"); + end = i; + start = i + 1; + ch = text[start]; + } + } + return end; + } + exports.FOLD_BLOCK = FOLD_BLOCK; + exports.FOLD_FLOW = FOLD_FLOW; + exports.FOLD_QUOTED = FOLD_QUOTED; + exports.foldFlowLines = foldFlowLines; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js +var require_stringifyString = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyString.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var foldFlowLines = require_foldFlowLines(); + var getFoldOptions = (ctx, isBlock) => ({ + indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart, + lineWidth: ctx.options.lineWidth, + minContentWidth: ctx.options.minContentWidth + }); + var containsDocumentMarker = (str) => /^(%|---|\.\.\.)/m.test(str); + function lineLengthOverLimit(str, lineWidth, indentLength) { + if (!lineWidth || lineWidth < 0) + return false; + const limit = lineWidth - indentLength; + const strLen = str.length; + if (strLen <= limit) + return false; + for (let i = 0, start = 0; i < strLen; ++i) { + if (str[i] === "\n") { + if (i - start > limit) + return true; + start = i + 1; + if (strLen - start <= limit) + return false; + } + } + return true; + } + function doubleQuotedString(value, ctx) { + const json = JSON.stringify(value); + if (ctx.options.doubleQuotedAsJSON) + return json; + const { implicitKey } = ctx; + const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength; + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + let str = ""; + let start = 0; + for (let i = 0, ch = json[i]; ch; ch = json[++i]) { + if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") { + str += json.slice(start, i) + "\\ "; + i += 1; + start = i; + ch = "\\"; + } + if (ch === "\\") + switch (json[i + 1]) { + case "u": + { + str += json.slice(start, i); + const code = json.substr(i + 2, 4); + switch (code) { + case "0000": + str += "\\0"; + break; + case "0007": + str += "\\a"; + break; + case "000b": + str += "\\v"; + break; + case "001b": + str += "\\e"; + break; + case "0085": + str += "\\N"; + break; + case "00a0": + str += "\\_"; + break; + case "2028": + str += "\\L"; + break; + case "2029": + str += "\\P"; + break; + default: + if (code.substr(0, 2) === "00") + str += "\\x" + code.substr(2); + else + str += json.substr(i, 6); + } + i += 5; + start = i + 1; + } + break; + case "n": + if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) { + i += 1; + } else { + str += json.slice(start, i) + "\n\n"; + while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') { + str += "\n"; + i += 2; + } + str += indent; + if (json[i + 2] === " ") + str += "\\"; + i += 1; + start = i + 1; + } + break; + default: + i += 1; + } + } + str = start ? str + json.slice(start) : json; + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false)); + } + function singleQuotedString(value, ctx) { + if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value)) + return doubleQuotedString(value, ctx); + const indent = ctx.indent || (containsDocumentMarker(value) ? " " : ""); + const res = "'" + value.replace(/'/g, "''").replace(/\n+/g, `$& +${indent}`) + "'"; + return ctx.implicitKey ? res : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function quotedString(value, ctx) { + const { singleQuote } = ctx.options; + let qs; + if (singleQuote === false) + qs = doubleQuotedString; + else { + const hasDouble = value.includes('"'); + const hasSingle = value.includes("'"); + if (hasDouble && !hasSingle) + qs = singleQuotedString; + else if (hasSingle && !hasDouble) + qs = doubleQuotedString; + else + qs = singleQuote ? singleQuotedString : doubleQuotedString; + } + return qs(value, ctx); + } + var blockEndNewlines; + try { + blockEndNewlines = new RegExp("(^|(?\n"; + let chomp; + let endStart; + for (endStart = value.length; endStart > 0; --endStart) { + const ch = value[endStart - 1]; + if (ch !== "\n" && ch !== " " && ch !== " ") + break; + } + let end = value.substring(endStart); + const endNlPos = end.indexOf("\n"); + if (endNlPos === -1) { + chomp = "-"; + } else if (value === end || endNlPos !== end.length - 1) { + chomp = "+"; + if (onChompKeep) + onChompKeep(); + } else { + chomp = ""; + } + if (end) { + value = value.slice(0, -end.length); + if (end[end.length - 1] === "\n") + end = end.slice(0, -1); + end = end.replace(blockEndNewlines, `$&${indent}`); + } + let startWithSpace = false; + let startEnd; + let startNlPos = -1; + for (startEnd = 0; startEnd < value.length; ++startEnd) { + const ch = value[startEnd]; + if (ch === " ") + startWithSpace = true; + else if (ch === "\n") + startNlPos = startEnd; + else + break; + } + let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd); + if (start) { + value = value.substring(start.length); + start = start.replace(/\n+/g, `$&${indent}`); + } + const indentSize = indent ? "2" : "1"; + let header = (startWithSpace ? indentSize : "") + chomp; + if (comment) { + header += " " + commentString(comment.replace(/ ?[\r\n]+/g, " ")); + if (onComment) + onComment(); + } + if (!literal) { + const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`); + let literalFallback = false; + const foldOptions = getFoldOptions(ctx, true); + if (blockQuote !== "folded" && type !== Scalar.Scalar.BLOCK_FOLDED) { + foldOptions.onOverflow = () => { + literalFallback = true; + }; + } + const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions); + if (!literalFallback) + return `>${header} +${indent}${body}`; + } + value = value.replace(/\n+/g, `$&${indent}`); + return `|${header} +${indent}${start}${value}${end}`; + } + function plainString(item, ctx, onComment, onChompKeep) { + const { type, value } = item; + const { actualString, implicitKey, indent, indentStep, inFlow } = ctx; + if (implicitKey && value.includes("\n") || inFlow && /[[\]{},]/.test(value)) { + return quotedString(value, ctx); + } + if (/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(value)) { + return implicitKey || inFlow || !value.includes("\n") ? quotedString(value, ctx) : blockString(item, ctx, onComment, onChompKeep); + } + if (!implicitKey && !inFlow && type !== Scalar.Scalar.PLAIN && value.includes("\n")) { + return blockString(item, ctx, onComment, onChompKeep); + } + if (containsDocumentMarker(value)) { + if (indent === "") { + ctx.forceBlockIndent = true; + return blockString(item, ctx, onComment, onChompKeep); + } else if (implicitKey && indent === indentStep) { + return quotedString(value, ctx); + } + } + const str = value.replace(/\n+/g, `$& +${indent}`); + if (actualString) { + const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str); + const { compat, tags } = ctx.doc.schema; + if (tags.some(test) || compat?.some(test)) + return quotedString(value, ctx); + } + return implicitKey ? str : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false)); + } + function stringifyString(item, ctx, onComment, onChompKeep) { + const { implicitKey, inFlow } = ctx; + const ss = typeof item.value === "string" ? item : Object.assign({}, item, { value: String(item.value) }); + let { type } = item; + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + if (/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(ss.value)) + type = Scalar.Scalar.QUOTE_DOUBLE; + } + const _stringify = (_type) => { + switch (_type) { + case Scalar.Scalar.BLOCK_FOLDED: + case Scalar.Scalar.BLOCK_LITERAL: + return implicitKey || inFlow ? quotedString(ss.value, ctx) : blockString(ss, ctx, onComment, onChompKeep); + case Scalar.Scalar.QUOTE_DOUBLE: + return doubleQuotedString(ss.value, ctx); + case Scalar.Scalar.QUOTE_SINGLE: + return singleQuotedString(ss.value, ctx); + case Scalar.Scalar.PLAIN: + return plainString(ss, ctx, onComment, onChompKeep); + default: + return null; + } + }; + let res = _stringify(type); + if (res === null) { + const { defaultKeyType, defaultStringType } = ctx.options; + const t = implicitKey && defaultKeyType || defaultStringType; + res = _stringify(t); + if (res === null) + throw new Error(`Unsupported default string type ${t}`); + } + return res; + } + exports.stringifyString = stringifyString; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js +var require_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringify.js"(exports) { + "use strict"; + var anchors = require_anchors(); + var identity = require_identity(); + var stringifyComment = require_stringifyComment(); + var stringifyString = require_stringifyString(); + function createStringifyContext(doc, options) { + const opt = Object.assign({ + blockQuote: true, + commentString: stringifyComment.stringifyComment, + defaultKeyType: null, + defaultStringType: "PLAIN", + directives: null, + doubleQuotedAsJSON: false, + doubleQuotedMinMultiLineLength: 40, + falseStr: "false", + flowCollectionPadding: true, + indentSeq: true, + lineWidth: 80, + minContentWidth: 20, + nullStr: "null", + simpleKeys: false, + singleQuote: null, + trailingComma: false, + trueStr: "true", + verifyAliasOrder: true + }, doc.schema.toStringOptions, options); + let inFlow; + switch (opt.collectionStyle) { + case "block": + inFlow = false; + break; + case "flow": + inFlow = true; + break; + default: + inFlow = null; + } + return { + anchors: /* @__PURE__ */ new Set(), + doc, + flowCollectionPadding: opt.flowCollectionPadding ? " " : "", + indent: "", + indentStep: typeof opt.indent === "number" ? " ".repeat(opt.indent) : " ", + inFlow, + options: opt + }; + } + function getTagObject(tags, item) { + if (item.tag) { + const match = tags.filter((t) => t.tag === item.tag); + if (match.length > 0) + return match.find((t) => t.format === item.format) ?? match[0]; + } + let tagObj = void 0; + let obj; + if (identity.isScalar(item)) { + obj = item.value; + let match = tags.filter((t) => t.identify?.(obj)); + if (match.length > 1) { + const testMatch = match.filter((t) => t.test); + if (testMatch.length > 0) + match = testMatch; + } + tagObj = match.find((t) => t.format === item.format) ?? match.find((t) => !t.format); + } else { + obj = item; + tagObj = tags.find((t) => t.nodeClass && obj instanceof t.nodeClass); + } + if (!tagObj) { + const name = obj?.constructor?.name ?? (obj === null ? "null" : typeof obj); + throw new Error(`Tag not resolved for ${name} value`); + } + return tagObj; + } + function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) { + if (!doc.directives) + return ""; + const props = []; + const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor; + if (anchor && anchors.anchorIsValid(anchor)) { + anchors$1.add(anchor); + props.push(`&${anchor}`); + } + const tag = node.tag ?? (tagObj.default ? null : tagObj.tag); + if (tag) + props.push(doc.directives.tagString(tag)); + return props.join(" "); + } + function stringify3(item, ctx, onComment, onChompKeep) { + if (identity.isPair(item)) + return item.toString(ctx, onComment, onChompKeep); + if (identity.isAlias(item)) { + if (ctx.doc.directives) + return item.toString(ctx); + if (ctx.resolvedAliases?.has(item)) { + throw new TypeError(`Cannot stringify circular structure without alias nodes`); + } else { + if (ctx.resolvedAliases) + ctx.resolvedAliases.add(item); + else + ctx.resolvedAliases = /* @__PURE__ */ new Set([item]); + item = item.resolve(ctx.doc); + } + } + let tagObj = void 0; + const node = identity.isNode(item) ? item : ctx.doc.createNode(item, { onTagObj: (o) => tagObj = o }); + tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node)); + const props = stringifyProps(node, tagObj, ctx); + if (props.length > 0) + ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1; + const str = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep); + if (!props) + return str; + return identity.isScalar(node) || str[0] === "{" || str[0] === "[" ? `${props} ${str}` : `${props} +${ctx.indent}${str}`; + } + exports.createStringifyContext = createStringifyContext; + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js +var require_stringifyPair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyPair.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyPair({ key, value }, ctx, onComment, onChompKeep) { + const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx; + let keyComment = identity.isNode(key) && key.comment || null; + if (simpleKeys) { + if (keyComment) { + throw new Error("With simple keys, key nodes cannot have comments"); + } + if (identity.isCollection(key) || !identity.isNode(key) && typeof key === "object") { + const msg = "With simple keys, collection cannot be used as a key value"; + throw new Error(msg); + } + } + let explicitKey = !simpleKeys && (!key || keyComment && value == null && !ctx.inFlow || identity.isCollection(key) || (identity.isScalar(key) ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL : typeof key === "object")); + ctx = Object.assign({}, ctx, { + allNullValues: false, + implicitKey: !explicitKey && (simpleKeys || !allNullValues), + indent: indent + indentStep + }); + let keyCommentDone = false; + let chompKeep = false; + let str = stringify3.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true); + if (!explicitKey && !ctx.inFlow && str.length > 1024) { + if (simpleKeys) + throw new Error("With simple keys, single line scalar must not span more than 1024 characters"); + explicitKey = true; + } + if (ctx.inFlow) { + if (allNullValues || value == null) { + if (keyCommentDone && onComment) + onComment(); + return str === "" ? "?" : explicitKey ? `? ${str}` : str; + } + } else if (allNullValues && !simpleKeys || value == null && explicitKey) { + str = `? ${str}`; + if (keyComment && !keyCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + if (keyCommentDone) + keyComment = null; + if (explicitKey) { + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + str = `? ${str} +${indent}:`; + } else { + str = `${str}:`; + if (keyComment) + str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment)); + } + let vsb, vcb, valueComment; + if (identity.isNode(value)) { + vsb = !!value.spaceBefore; + vcb = value.commentBefore; + valueComment = value.comment; + } else { + vsb = false; + vcb = null; + valueComment = null; + if (value && typeof value === "object") + value = doc.createNode(value); + } + ctx.implicitKey = false; + if (!explicitKey && !keyComment && identity.isScalar(value)) + ctx.indentAtStart = str.length + 1; + chompKeep = false; + if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) { + ctx.indent = ctx.indent.substring(2); + } + let valueCommentDone = false; + const valueStr = stringify3.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true); + let ws = " "; + if (keyComment || vsb || vcb) { + ws = vsb ? "\n" : ""; + if (vcb) { + const cs = commentString(vcb); + ws += ` +${stringifyComment.indentComment(cs, ctx.indent)}`; + } + if (valueStr === "" && !ctx.inFlow) { + if (ws === "\n" && valueComment) + ws = "\n\n"; + } else { + ws += ` +${ctx.indent}`; + } + } else if (!explicitKey && identity.isCollection(value)) { + const vs0 = valueStr[0]; + const nl0 = valueStr.indexOf("\n"); + const hasNewline = nl0 !== -1; + const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0; + if (hasNewline || !flow) { + let hasPropsLine = false; + if (hasNewline && (vs0 === "&" || vs0 === "!")) { + let sp0 = valueStr.indexOf(" "); + if (vs0 === "&" && sp0 !== -1 && sp0 < nl0 && valueStr[sp0 + 1] === "!") { + sp0 = valueStr.indexOf(" ", sp0 + 1); + } + if (sp0 === -1 || nl0 < sp0) + hasPropsLine = true; + } + if (!hasPropsLine) + ws = ` +${ctx.indent}`; + } + } else if (valueStr === "" || valueStr[0] === "\n") { + ws = ""; + } + str += ws + valueStr; + if (ctx.inFlow) { + if (valueCommentDone && onComment) + onComment(); + } else if (valueComment && !valueCommentDone) { + str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment)); + } else if (chompKeep && onChompKeep) { + onChompKeep(); + } + return str; + } + exports.stringifyPair = stringifyPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js +var require_log = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/log.js"(exports) { + "use strict"; + var node_process = __require("process"); + function debug(logLevel, ...messages) { + if (logLevel === "debug") + console.log(...messages); + } + function warn(logLevel, warning) { + if (logLevel === "debug" || logLevel === "warn") { + if (typeof node_process.emitWarning === "function") + node_process.emitWarning(warning); + else + console.warn(warning); + } + } + exports.debug = debug; + exports.warn = warn; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js +var require_merge = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/merge.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var MERGE_KEY = "<<"; + var merge = { + identify: (value) => value === MERGE_KEY || typeof value === "symbol" && value.description === MERGE_KEY, + default: "key", + tag: "tag:yaml.org,2002:merge", + test: /^<<$/, + resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), { + addToJSMap: addMergeToJSMap + }), + stringify: () => MERGE_KEY + }; + var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default); + function addMergeToJSMap(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (identity.isSeq(source)) + for (const it of source.items) + mergeValue(ctx, map, it); + else if (Array.isArray(source)) + for (const it of source) + mergeValue(ctx, map, it); + else + mergeValue(ctx, map, source); + } + function mergeValue(ctx, map, value) { + const source = resolveAliasValue(ctx, value); + if (!identity.isMap(source)) + throw new Error("Merge sources must be maps or map aliases"); + const srcMap = source.toJSON(null, ctx, Map); + for (const [key, value2] of srcMap) { + if (map instanceof Map) { + if (!map.has(key)) + map.set(key, value2); + } else if (map instanceof Set) { + map.add(key); + } else if (!Object.prototype.hasOwnProperty.call(map, key)) { + Object.defineProperty(map, key, { + value: value2, + writable: true, + enumerable: true, + configurable: true + }); + } + } + return map; + } + function resolveAliasValue(ctx, value) { + return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value; + } + exports.addMergeToJSMap = addMergeToJSMap; + exports.isMergeKey = isMergeKey; + exports.merge = merge; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js +var require_addPairToJSMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/addPairToJSMap.js"(exports) { + "use strict"; + var log = require_log(); + var merge = require_merge(); + var stringify3 = require_stringify(); + var identity = require_identity(); + var toJS = require_toJS(); + function addPairToJSMap(ctx, map, { key, value }) { + if (identity.isNode(key) && key.addToJSMap) + key.addToJSMap(ctx, map, value); + else if (merge.isMergeKey(ctx, key)) + merge.addMergeToJSMap(ctx, map, value); + else { + const jsKey = toJS.toJS(key, "", ctx); + if (map instanceof Map) { + map.set(jsKey, toJS.toJS(value, jsKey, ctx)); + } else if (map instanceof Set) { + map.add(jsKey); + } else { + const stringKey = stringifyKey(key, jsKey, ctx); + const jsValue = toJS.toJS(value, stringKey, ctx); + if (stringKey in map) + Object.defineProperty(map, stringKey, { + value: jsValue, + writable: true, + enumerable: true, + configurable: true + }); + else + map[stringKey] = jsValue; + } + } + return map; + } + function stringifyKey(key, jsKey, ctx) { + if (jsKey === null) + return ""; + if (typeof jsKey !== "object") + return String(jsKey); + if (identity.isNode(key) && ctx?.doc) { + const strCtx = stringify3.createStringifyContext(ctx.doc, {}); + strCtx.anchors = /* @__PURE__ */ new Set(); + for (const node of ctx.anchors.keys()) + strCtx.anchors.add(node.anchor); + strCtx.inFlow = true; + strCtx.inStringifyKey = true; + const strKey = key.toString(strCtx); + if (!ctx.mapKeyWarned) { + let jsonStr = JSON.stringify(strKey); + if (jsonStr.length > 40) + jsonStr = jsonStr.substring(0, 36) + '..."'; + log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); + ctx.mapKeyWarned = true; + } + return strKey; + } + return JSON.stringify(jsKey); + } + exports.addPairToJSMap = addPairToJSMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js +var require_Pair = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/Pair.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyPair = require_stringifyPair(); + var addPairToJSMap = require_addPairToJSMap(); + var identity = require_identity(); + function createPair(key, value, ctx) { + const k = createNode.createNode(key, void 0, ctx); + const v = createNode.createNode(value, void 0, ctx); + return new Pair(k, v); + } + var Pair = class _Pair { + constructor(key, value = null) { + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR }); + this.key = key; + this.value = value; + } + clone(schema) { + let { key, value } = this; + if (identity.isNode(key)) + key = key.clone(schema); + if (identity.isNode(value)) + value = value.clone(schema); + return new _Pair(key, value); + } + toJSON(_, ctx) { + const pair = ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + return addPairToJSMap.addPairToJSMap(ctx, pair, this); + } + toString(ctx, onComment, onChompKeep) { + return ctx?.doc ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep) : JSON.stringify(this); + } + }; + exports.Pair = Pair; + exports.createPair = createPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js +var require_stringifyCollection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyCollection(collection, ctx, options) { + const flow = ctx.inFlow ?? collection.flow; + const stringify4 = flow ? stringifyFlowCollection : stringifyBlockCollection; + return stringify4(collection, ctx, options); + } + function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) { + const { indent, options: { commentString } } = ctx; + const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null }); + let chompKeep = false; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment2 = null; + if (identity.isNode(item)) { + if (!chompKeep && item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, chompKeep); + if (item.comment) + comment2 = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (!chompKeep && ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, chompKeep); + } + } + chompKeep = false; + let str2 = stringify3.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true); + if (comment2) + str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2)); + if (chompKeep && comment2) + chompKeep = false; + lines.push(blockItemPrefix + str2); + } + let str; + if (lines.length === 0) { + str = flowChars.start + flowChars.end; + } else { + str = lines[0]; + for (let i = 1; i < lines.length; ++i) { + const line = lines[i]; + str += line ? ` +${indent}${line}` : "\n"; + } + } + if (comment) { + str += "\n" + stringifyComment.indentComment(commentString(comment), indent); + if (onComment) + onComment(); + } else if (chompKeep && onChompKeep) + onChompKeep(); + return str; + } + function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) { + const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx; + itemIndent += indentStep; + const itemCtx = Object.assign({}, ctx, { + indent: itemIndent, + inFlow: true, + type: null + }); + let reqNewline = false; + let linesAtValue = 0; + const lines = []; + for (let i = 0; i < items.length; ++i) { + const item = items[i]; + let comment = null; + if (identity.isNode(item)) { + if (item.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, item.commentBefore, false); + if (item.comment) + comment = item.comment; + } else if (identity.isPair(item)) { + const ik = identity.isNode(item.key) ? item.key : null; + if (ik) { + if (ik.spaceBefore) + lines.push(""); + addCommentBefore(ctx, lines, ik.commentBefore, false); + if (ik.comment) + reqNewline = true; + } + const iv = identity.isNode(item.value) ? item.value : null; + if (iv) { + if (iv.comment) + comment = iv.comment; + if (iv.commentBefore) + reqNewline = true; + } else if (item.value == null && ik?.comment) { + comment = ik.comment; + } + } + if (comment) + reqNewline = true; + let str = stringify3.stringify(item, itemCtx, () => comment = null); + reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n")); + if (i < items.length - 1) { + str += ","; + } else if (ctx.options.trailingComma) { + if (ctx.options.lineWidth > 0) { + reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth); + } + if (reqNewline) { + str += ","; + } + } + if (comment) + str += stringifyComment.lineComment(str, itemIndent, commentString(comment)); + lines.push(str); + linesAtValue = lines.length; + } + const { start, end } = flowChars; + if (lines.length === 0) { + return start + end; + } else { + if (!reqNewline) { + const len = lines.reduce((sum, line) => sum + line.length + 2, 2); + reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth; + } + if (reqNewline) { + let str = start; + for (const line of lines) + str += line ? ` +${indentStep}${indent}${line}` : "\n"; + return `${str} +${indent}${end}`; + } else { + return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`; + } + } + } + function addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) { + if (comment && chompKeep) + comment = comment.replace(/^\n+/, ""); + if (comment) { + const ic = stringifyComment.indentComment(commentString(comment), indent); + lines.push(ic.trimStart()); + } + } + exports.stringifyCollection = stringifyCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js +var require_YAMLMap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLMap.js"(exports) { + "use strict"; + var stringifyCollection = require_stringifyCollection(); + var addPairToJSMap = require_addPairToJSMap(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + function findPair(items, key) { + const k = identity.isScalar(key) ? key.value : key; + for (const it of items) { + if (identity.isPair(it)) { + if (it.key === key || it.key === k) + return it; + if (identity.isScalar(it.key) && it.key.value === k) + return it; + } + } + return void 0; + } + var YAMLMap = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:map"; + } + constructor(schema) { + super(identity.MAP, schema); + this.items = []; + } + /** + * A generic collection parsing method that can be extended + * to other node classes that inherit from YAMLMap + */ + static from(schema, obj, ctx) { + const { keepUndefined, replacer } = ctx; + const map = new this(schema); + const add = (key, value) => { + if (typeof replacer === "function") + value = replacer.call(obj, key, value); + else if (Array.isArray(replacer) && !replacer.includes(key)) + return; + if (value !== void 0 || keepUndefined) + map.items.push(Pair.createPair(key, value, ctx)); + }; + if (obj instanceof Map) { + for (const [key, value] of obj) + add(key, value); + } else if (obj && typeof obj === "object") { + for (const key of Object.keys(obj)) + add(key, obj[key]); + } + if (typeof schema.sortMapEntries === "function") { + map.items.sort(schema.sortMapEntries); + } + return map; + } + /** + * Adds a value to the collection. + * + * @param overwrite - If not set `true`, using a key that is already in the + * collection will throw. Otherwise, overwrites the previous value. + */ + add(pair, overwrite) { + let _pair; + if (identity.isPair(pair)) + _pair = pair; + else if (!pair || typeof pair !== "object" || !("key" in pair)) { + _pair = new Pair.Pair(pair, pair?.value); + } else + _pair = new Pair.Pair(pair.key, pair.value); + const prev = findPair(this.items, _pair.key); + const sortEntries = this.schema?.sortMapEntries; + if (prev) { + if (!overwrite) + throw new Error(`Key ${_pair.key} already set`); + if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value)) + prev.value.value = _pair.value; + else + prev.value = _pair.value; + } else if (sortEntries) { + const i = this.items.findIndex((item) => sortEntries(_pair, item) < 0); + if (i === -1) + this.items.push(_pair); + else + this.items.splice(i, 0, _pair); + } else { + this.items.push(_pair); + } + } + delete(key) { + const it = findPair(this.items, key); + if (!it) + return false; + const del = this.items.splice(this.items.indexOf(it), 1); + return del.length > 0; + } + get(key, keepScalar) { + const it = findPair(this.items, key); + const node = it?.value; + return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? void 0; + } + has(key) { + return !!findPair(this.items, key); + } + set(key, value) { + this.add(new Pair.Pair(key, value), true); + } + /** + * @param ctx - Conversion context, originally set in Document#toJS() + * @param {Class} Type - If set, forces the returned collection type + * @returns Instance of Type, Map, or Object + */ + toJSON(_, ctx, Type) { + const map = Type ? new Type() : ctx?.mapAsMap ? /* @__PURE__ */ new Map() : {}; + if (ctx?.onCreate) + ctx.onCreate(map); + for (const item of this.items) + addPairToJSMap.addPairToJSMap(ctx, map, item); + return map; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + for (const item of this.items) { + if (!identity.isPair(item)) + throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`); + } + if (!ctx.allNullValues && this.hasAllNullValues(false)) + ctx = Object.assign({}, ctx, { allNullValues: true }); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "", + flowChars: { start: "{", end: "}" }, + itemIndent: ctx.indent || "", + onChompKeep, + onComment + }); + } + }; + exports.YAMLMap = YAMLMap; + exports.findPair = findPair; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js +var require_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/map.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLMap = require_YAMLMap(); + var map = { + collection: "map", + default: true, + nodeClass: YAMLMap.YAMLMap, + tag: "tag:yaml.org,2002:map", + resolve(map2, onError) { + if (!identity.isMap(map2)) + onError("Expected a mapping for this tag"); + return map2; + }, + createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx) + }; + exports.map = map; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js +var require_YAMLSeq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/nodes/YAMLSeq.js"(exports) { + "use strict"; + var createNode = require_createNode(); + var stringifyCollection = require_stringifyCollection(); + var Collection = require_Collection(); + var identity = require_identity(); + var Scalar = require_Scalar(); + var toJS = require_toJS(); + var YAMLSeq = class extends Collection.Collection { + static get tagName() { + return "tag:yaml.org,2002:seq"; + } + constructor(schema) { + super(identity.SEQ, schema); + this.items = []; + } + add(value) { + this.items.push(value); + } + /** + * Removes a value from the collection. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + * + * @returns `true` if the item was found and removed. + */ + delete(key) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return false; + const del = this.items.splice(idx, 1); + return del.length > 0; + } + get(key, keepScalar) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + return void 0; + const it = this.items[idx]; + return !keepScalar && identity.isScalar(it) ? it.value : it; + } + /** + * Checks if the collection includes a value with the key `key`. + * + * `key` must contain a representation of an integer for this to succeed. + * It may be wrapped in a `Scalar`. + */ + has(key) { + const idx = asItemIndex(key); + return typeof idx === "number" && idx < this.items.length; + } + /** + * Sets a value in this collection. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + * + * If `key` does not contain a representation of an integer, this will throw. + * It may be wrapped in a `Scalar`. + */ + set(key, value) { + const idx = asItemIndex(key); + if (typeof idx !== "number") + throw new Error(`Expected a valid index, not ${key}.`); + const prev = this.items[idx]; + if (identity.isScalar(prev) && Scalar.isScalarValue(value)) + prev.value = value; + else + this.items[idx] = value; + } + toJSON(_, ctx) { + const seq = []; + if (ctx?.onCreate) + ctx.onCreate(seq); + let i = 0; + for (const item of this.items) + seq.push(toJS.toJS(item, String(i++), ctx)); + return seq; + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + return stringifyCollection.stringifyCollection(this, ctx, { + blockItemPrefix: "- ", + flowChars: { start: "[", end: "]" }, + itemIndent: (ctx.indent || "") + " ", + onChompKeep, + onComment + }); + } + static from(schema, obj, ctx) { + const { replacer } = ctx; + const seq = new this(schema); + if (obj && Symbol.iterator in Object(obj)) { + let i = 0; + for (let it of obj) { + if (typeof replacer === "function") { + const key = obj instanceof Set ? it : String(i++); + it = replacer.call(obj, key, it); + } + seq.items.push(createNode.createNode(it, void 0, ctx)); + } + } + return seq; + } + }; + function asItemIndex(key) { + let idx = identity.isScalar(key) ? key.value : key; + if (idx && typeof idx === "string") + idx = Number(idx); + return typeof idx === "number" && Number.isInteger(idx) && idx >= 0 ? idx : null; + } + exports.YAMLSeq = YAMLSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js +var require_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/seq.js"(exports) { + "use strict"; + var identity = require_identity(); + var YAMLSeq = require_YAMLSeq(); + var seq = { + collection: "seq", + default: true, + nodeClass: YAMLSeq.YAMLSeq, + tag: "tag:yaml.org,2002:seq", + resolve(seq2, onError) { + if (!identity.isSeq(seq2)) + onError("Expected a sequence for this tag"); + return seq2; + }, + createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx) + }; + exports.seq = seq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js +var require_string = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/string.js"(exports) { + "use strict"; + var stringifyString = require_stringifyString(); + var string = { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify(item, ctx, onComment, onChompKeep) { + ctx = Object.assign({ actualString: true }, ctx); + return stringifyString.stringifyString(item, ctx, onComment, onChompKeep); + } + }; + exports.string = string; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js +var require_null = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/common/null.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var nullTag = { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^(?:~|[Nn]ull|NULL)?$/, + resolve: () => new Scalar.Scalar(null), + stringify: ({ source }, ctx) => typeof source === "string" && nullTag.test.test(source) ? source : ctx.options.nullStr + }; + exports.nullTag = nullTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js +var require_bool = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var boolTag = { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/, + resolve: (str) => new Scalar.Scalar(str[0] === "t" || str[0] === "T"), + stringify({ source, value }, ctx) { + if (source && boolTag.test.test(source)) { + const sv = source[0] === "t" || source[0] === "T"; + if (value === sv) + return source; + } + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + }; + exports.boolTag = boolTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js +var require_stringifyNumber = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyNumber.js"(exports) { + "use strict"; + function stringifyNumber({ format, minFractionDigits, tag, value }) { + if (typeof value === "bigint") + return String(value); + const num = typeof value === "number" ? value : Number(value); + if (!isFinite(num)) + return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf"; + let n = Object.is(value, -0) ? "-0" : JSON.stringify(value); + if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) { + let i = n.indexOf("."); + if (i < 0) { + i = n.length; + n += "."; + } + let d = minFractionDigits - (n.length - i - 1); + while (d-- > 0) + n += "0"; + } + return n; + } + exports.stringifyNumber = stringifyNumber; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js +var require_float = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str)); + const dot = str.indexOf("."); + if (dot !== -1 && str[str.length - 1] === "0") + node.minFractionDigits = str.length - dot - 1; + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js +var require_int = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + var intResolve = (str, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix); + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value) && value >= 0) + return prefix + value.toString(radix); + return stringifyNumber.stringifyNumber(node); + } + var intOct = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^0o[0-7]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt), + stringify: (node) => intStringify(node, 8, "0o") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9]+$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: (value) => intIdentify(value) && value >= 0, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^0x[0-9a-fA-F]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js +var require_schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/core/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.boolTag, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js +var require_schema2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/json/schema.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var map = require_map(); + var seq = require_seq(); + function intIdentify(value) { + return typeof value === "bigint" || Number.isInteger(value); + } + var stringifyJSON = ({ value }) => JSON.stringify(value); + var jsonScalars = [ + { + identify: (value) => typeof value === "string", + default: true, + tag: "tag:yaml.org,2002:str", + resolve: (str) => str, + stringify: stringifyJSON + }, + { + identify: (value) => value == null, + createNode: () => new Scalar.Scalar(null), + default: true, + tag: "tag:yaml.org,2002:null", + test: /^null$/, + resolve: () => null, + stringify: stringifyJSON + }, + { + identify: (value) => typeof value === "boolean", + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^true$|^false$/, + resolve: (str) => str === "true", + stringify: stringifyJSON + }, + { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^-?(?:0|[1-9][0-9]*)$/, + resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10), + stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value) + }, + { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/, + resolve: (str) => parseFloat(str), + stringify: stringifyJSON + } + ]; + var jsonError = { + default: true, + tag: "", + test: /^/, + resolve(str, onError) { + onError(`Unresolved plain scalar ${JSON.stringify(str)}`); + return str; + } + }; + var schema = [map.map, seq.seq].concat(jsonScalars, jsonError); + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js +var require_binary = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/binary.js"(exports) { + "use strict"; + var node_buffer = __require("buffer"); + var Scalar = require_Scalar(); + var stringifyString = require_stringifyString(); + var binary = { + identify: (value) => value instanceof Uint8Array, + // Buffer inherits from Uint8Array + default: false, + tag: "tag:yaml.org,2002:binary", + /** + * Returns a Buffer in node and an Uint8Array in browsers + * + * To use the resulting buffer as an image, you'll want to do something like: + * + * const blob = new Blob([buffer], { type: 'image/jpeg' }) + * document.querySelector('#photo').src = URL.createObjectURL(blob) + */ + resolve(src, onError) { + if (typeof node_buffer.Buffer === "function") { + return node_buffer.Buffer.from(src, "base64"); + } else if (typeof atob === "function") { + const str = atob(src.replace(/[\n\r]/g, "")); + const buffer = new Uint8Array(str.length); + for (let i = 0; i < str.length; ++i) + buffer[i] = str.charCodeAt(i); + return buffer; + } else { + onError("This environment does not support reading binary tags; either Buffer or atob is required"); + return src; + } + }, + stringify({ comment, type, value }, ctx, onComment, onChompKeep) { + if (!value) + return ""; + const buf = value; + let str; + if (typeof node_buffer.Buffer === "function") { + str = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64"); + } else if (typeof btoa === "function") { + let s = ""; + for (let i = 0; i < buf.length; ++i) + s += String.fromCharCode(buf[i]); + str = btoa(s); + } else { + throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required"); + } + type ?? (type = Scalar.Scalar.BLOCK_LITERAL); + if (type !== Scalar.Scalar.QUOTE_DOUBLE) { + const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth); + const n = Math.ceil(str.length / lineWidth); + const lines = new Array(n); + for (let i = 0, o = 0; i < n; ++i, o += lineWidth) { + lines[i] = str.substr(o, lineWidth); + } + str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " "); + } + return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep); + } + }; + exports.binary = binary; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +var require_pairs = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/pairs.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLSeq = require_YAMLSeq(); + function resolvePairs(seq, onError) { + if (identity.isSeq(seq)) { + for (let i = 0; i < seq.items.length; ++i) { + let item = seq.items[i]; + if (identity.isPair(item)) + continue; + else if (identity.isMap(item)) { + if (item.items.length > 1) + onError("Each pair must have its own sequence indicator"); + const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null)); + if (item.commentBefore) + pair.key.commentBefore = pair.key.commentBefore ? `${item.commentBefore} +${pair.key.commentBefore}` : item.commentBefore; + if (item.comment) { + const cn = pair.value ?? pair.key; + cn.comment = cn.comment ? `${item.comment} +${cn.comment}` : item.comment; + } + item = pair; + } + seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item); + } + } else + onError("Expected a sequence for this tag"); + return seq; + } + function createPairs(schema, iterable, ctx) { + const { replacer } = ctx; + const pairs2 = new YAMLSeq.YAMLSeq(schema); + pairs2.tag = "tag:yaml.org,2002:pairs"; + let i = 0; + if (iterable && Symbol.iterator in Object(iterable)) + for (let it of iterable) { + if (typeof replacer === "function") + it = replacer.call(iterable, String(i++), it); + let key, value; + if (Array.isArray(it)) { + if (it.length === 2) { + key = it[0]; + value = it[1]; + } else + throw new TypeError(`Expected [key, value] tuple: ${it}`); + } else if (it && it instanceof Object) { + const keys = Object.keys(it); + if (keys.length === 1) { + key = keys[0]; + value = it[key]; + } else { + throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`); + } + } else { + key = it; + } + pairs2.items.push(Pair.createPair(key, value, ctx)); + } + return pairs2; + } + var pairs = { + collection: "seq", + default: false, + tag: "tag:yaml.org,2002:pairs", + resolve: resolvePairs, + createNode: createPairs + }; + exports.createPairs = createPairs; + exports.pairs = pairs; + exports.resolvePairs = resolvePairs; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js +var require_omap = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/omap.js"(exports) { + "use strict"; + var identity = require_identity(); + var toJS = require_toJS(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var pairs = require_pairs(); + var YAMLOMap = class _YAMLOMap extends YAMLSeq.YAMLSeq { + constructor() { + super(); + this.add = YAMLMap.YAMLMap.prototype.add.bind(this); + this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this); + this.get = YAMLMap.YAMLMap.prototype.get.bind(this); + this.has = YAMLMap.YAMLMap.prototype.has.bind(this); + this.set = YAMLMap.YAMLMap.prototype.set.bind(this); + this.tag = _YAMLOMap.tag; + } + /** + * If `ctx` is given, the return type is actually `Map`, + * but TypeScript won't allow widening the signature of a child method. + */ + toJSON(_, ctx) { + if (!ctx) + return super.toJSON(_); + const map = /* @__PURE__ */ new Map(); + if (ctx?.onCreate) + ctx.onCreate(map); + for (const pair of this.items) { + let key, value; + if (identity.isPair(pair)) { + key = toJS.toJS(pair.key, "", ctx); + value = toJS.toJS(pair.value, key, ctx); + } else { + key = toJS.toJS(pair, "", ctx); + } + if (map.has(key)) + throw new Error("Ordered maps must not include duplicate keys"); + map.set(key, value); + } + return map; + } + static from(schema, iterable, ctx) { + const pairs$1 = pairs.createPairs(schema, iterable, ctx); + const omap2 = new this(); + omap2.items = pairs$1.items; + return omap2; + } + }; + YAMLOMap.tag = "tag:yaml.org,2002:omap"; + var omap = { + collection: "seq", + identify: (value) => value instanceof Map, + nodeClass: YAMLOMap, + default: false, + tag: "tag:yaml.org,2002:omap", + resolve(seq, onError) { + const pairs$1 = pairs.resolvePairs(seq, onError); + const seenKeys = []; + for (const { key } of pairs$1.items) { + if (identity.isScalar(key)) { + if (seenKeys.includes(key.value)) { + onError(`Ordered maps must not include duplicate keys: ${key.value}`); + } else { + seenKeys.push(key.value); + } + } + } + return Object.assign(new YAMLOMap(), pairs$1); + }, + createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx) + }; + exports.YAMLOMap = YAMLOMap; + exports.omap = omap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js +var require_bool2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/bool.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function boolStringify({ value, source }, ctx) { + const boolObj = value ? trueTag : falseTag; + if (source && boolObj.test.test(source)) + return source; + return value ? ctx.options.trueStr : ctx.options.falseStr; + } + var trueTag = { + identify: (value) => value === true, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/, + resolve: () => new Scalar.Scalar(true), + stringify: boolStringify + }; + var falseTag = { + identify: (value) => value === false, + default: true, + tag: "tag:yaml.org,2002:bool", + test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/, + resolve: () => new Scalar.Scalar(false), + stringify: boolStringify + }; + exports.falseTag = falseTag; + exports.trueTag = trueTag; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js +var require_float2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/float.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var stringifyNumber = require_stringifyNumber(); + var floatNaN = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/, + resolve: (str) => str.slice(-3).toLowerCase() === "nan" ? NaN : str[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY, + stringify: stringifyNumber.stringifyNumber + }; + var floatExp = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "EXP", + test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/, + resolve: (str) => parseFloat(str.replace(/_/g, "")), + stringify(node) { + const num = Number(node.value); + return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node); + } + }; + var float = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/, + resolve(str) { + const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, ""))); + const dot = str.indexOf("."); + if (dot !== -1) { + const f = str.substring(dot + 1).replace(/_/g, ""); + if (f[f.length - 1] === "0") + node.minFractionDigits = f.length; + } + return node; + }, + stringify: stringifyNumber.stringifyNumber + }; + exports.float = float; + exports.floatExp = floatExp; + exports.floatNaN = floatNaN; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js +var require_int2 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/int.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value); + function intResolve(str, offset, radix, { intAsBigInt }) { + const sign = str[0]; + if (sign === "-" || sign === "+") + offset += 1; + str = str.substring(offset).replace(/_/g, ""); + if (intAsBigInt) { + switch (radix) { + case 2: + str = `0b${str}`; + break; + case 8: + str = `0o${str}`; + break; + case 16: + str = `0x${str}`; + break; + } + const n2 = BigInt(str); + return sign === "-" ? BigInt(-1) * n2 : n2; + } + const n = parseInt(str, radix); + return sign === "-" ? -1 * n : n; + } + function intStringify(node, radix, prefix) { + const { value } = node; + if (intIdentify(value)) { + const str = value.toString(radix); + return value < 0 ? "-" + prefix + str.substr(1) : prefix + str; + } + return stringifyNumber.stringifyNumber(node); + } + var intBin = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "BIN", + test: /^[-+]?0b[0-1_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt), + stringify: (node) => intStringify(node, 2, "0b") + }; + var intOct = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "OCT", + test: /^[-+]?0[0-7_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt), + stringify: (node) => intStringify(node, 8, "0") + }; + var int = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + test: /^[-+]?[0-9][0-9_]*$/, + resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt), + stringify: stringifyNumber.stringifyNumber + }; + var intHex = { + identify: intIdentify, + default: true, + tag: "tag:yaml.org,2002:int", + format: "HEX", + test: /^[-+]?0x[0-9a-fA-F_]+$/, + resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt), + stringify: (node) => intStringify(node, 16, "0x") + }; + exports.int = int; + exports.intBin = intBin; + exports.intHex = intHex; + exports.intOct = intOct; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js +var require_set = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/set.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSet = class _YAMLSet extends YAMLMap.YAMLMap { + constructor(schema) { + super(schema); + this.tag = _YAMLSet.tag; + } + add(key) { + let pair; + if (identity.isPair(key)) + pair = key; + else if (key && typeof key === "object" && "key" in key && "value" in key && key.value === null) + pair = new Pair.Pair(key.key, null); + else + pair = new Pair.Pair(key, null); + const prev = YAMLMap.findPair(this.items, pair.key); + if (!prev) + this.items.push(pair); + } + /** + * If `keepPair` is `true`, returns the Pair matching `key`. + * Otherwise, returns the value of that Pair's key. + */ + get(key, keepPair) { + const pair = YAMLMap.findPair(this.items, key); + return !keepPair && identity.isPair(pair) ? identity.isScalar(pair.key) ? pair.key.value : pair.key : pair; + } + set(key, value) { + if (typeof value !== "boolean") + throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`); + const prev = YAMLMap.findPair(this.items, key); + if (prev && !value) { + this.items.splice(this.items.indexOf(prev), 1); + } else if (!prev && value) { + this.items.push(new Pair.Pair(key)); + } + } + toJSON(_, ctx) { + return super.toJSON(_, ctx, Set); + } + toString(ctx, onComment, onChompKeep) { + if (!ctx) + return JSON.stringify(this); + if (this.hasAllNullValues(true)) + return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep); + else + throw new Error("Set items must all have null values"); + } + static from(schema, iterable, ctx) { + const { replacer } = ctx; + const set2 = new this(schema); + if (iterable && Symbol.iterator in Object(iterable)) + for (let value of iterable) { + if (typeof replacer === "function") + value = replacer.call(iterable, value, value); + set2.items.push(Pair.createPair(value, null, ctx)); + } + return set2; + } + }; + YAMLSet.tag = "tag:yaml.org,2002:set"; + var set = { + collection: "map", + identify: (value) => value instanceof Set, + nodeClass: YAMLSet, + default: false, + tag: "tag:yaml.org,2002:set", + createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx), + resolve(map, onError) { + if (identity.isMap(map)) { + if (map.hasAllNullValues(true)) + return Object.assign(new YAMLSet(), map); + else + onError("Set items must all have null values"); + } else + onError("Expected a mapping for this tag"); + return map; + } + }; + exports.YAMLSet = YAMLSet; + exports.set = set; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +var require_timestamp = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) { + "use strict"; + var stringifyNumber = require_stringifyNumber(); + function parseSexagesimal(str, asBigInt) { + const sign = str[0]; + const parts = sign === "-" || sign === "+" ? str.substring(1) : str; + const num = (n) => asBigInt ? BigInt(n) : Number(n); + const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0)); + return sign === "-" ? num(-1) * res : res; + } + function stringifySexagesimal(node) { + let { value } = node; + let num = (n) => n; + if (typeof value === "bigint") + num = (n) => BigInt(n); + else if (isNaN(value) || !isFinite(value)) + return stringifyNumber.stringifyNumber(node); + let sign = ""; + if (value < 0) { + sign = "-"; + value *= num(-1); + } + const _60 = num(60); + const parts = [value % _60]; + if (value < 60) { + parts.unshift(0); + } else { + value = (value - parts[0]) / _60; + parts.unshift(value % _60); + if (value >= 60) { + value = (value - parts[0]) / _60; + parts.unshift(value); + } + } + return sign + parts.map((n) => String(n).padStart(2, "0")).join(":").replace(/000000\d*$/, ""); + } + var intTime = { + identify: (value) => typeof value === "bigint" || Number.isInteger(value), + default: true, + tag: "tag:yaml.org,2002:int", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/, + resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt), + stringify: stringifySexagesimal + }; + var floatTime = { + identify: (value) => typeof value === "number", + default: true, + tag: "tag:yaml.org,2002:float", + format: "TIME", + test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/, + resolve: (str) => parseSexagesimal(str, false), + stringify: stringifySexagesimal + }; + var timestamp = { + identify: (value) => value instanceof Date, + default: true, + tag: "tag:yaml.org,2002:timestamp", + // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part + // may be omitted altogether, resulting in a date format. In such a case, the time part is + // assumed to be 00:00:00Z (start of day, UTC). + test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"), + resolve(str) { + const match = str.match(timestamp.test); + if (!match) + throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd"); + const [, year, month, day, hour, minute, second] = match.map(Number); + const millisec = match[7] ? Number((match[7] + "00").substr(1, 3)) : 0; + let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec); + const tz = match[8]; + if (tz && tz !== "Z") { + let d = parseSexagesimal(tz, false); + if (Math.abs(d) < 30) + d *= 60; + date -= 6e4 * d; + } + return new Date(date); + }, + stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\.000Z$/, "") ?? "" + }; + exports.floatTime = floatTime; + exports.intTime = intTime; + exports.timestamp = timestamp; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js +var require_schema3 = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/yaml-1.1/schema.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var binary = require_binary(); + var bool = require_bool2(); + var float = require_float2(); + var int = require_int2(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var set = require_set(); + var timestamp = require_timestamp(); + var schema = [ + map.map, + seq.seq, + string.string, + _null.nullTag, + bool.trueTag, + bool.falseTag, + int.intBin, + int.intOct, + int.int, + int.intHex, + float.floatNaN, + float.floatExp, + float.float, + binary.binary, + merge.merge, + omap.omap, + pairs.pairs, + set.set, + timestamp.intTime, + timestamp.floatTime, + timestamp.timestamp + ]; + exports.schema = schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js +var require_tags = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/tags.js"(exports) { + "use strict"; + var map = require_map(); + var _null = require_null(); + var seq = require_seq(); + var string = require_string(); + var bool = require_bool(); + var float = require_float(); + var int = require_int(); + var schema = require_schema(); + var schema$1 = require_schema2(); + var binary = require_binary(); + var merge = require_merge(); + var omap = require_omap(); + var pairs = require_pairs(); + var schema$2 = require_schema3(); + var set = require_set(); + var timestamp = require_timestamp(); + var schemas = /* @__PURE__ */ new Map([ + ["core", schema.schema], + ["failsafe", [map.map, seq.seq, string.string]], + ["json", schema$1.schema], + ["yaml11", schema$2.schema], + ["yaml-1.1", schema$2.schema] + ]); + var tagsByName = { + binary: binary.binary, + bool: bool.boolTag, + float: float.float, + floatExp: float.floatExp, + floatNaN: float.floatNaN, + floatTime: timestamp.floatTime, + int: int.int, + intHex: int.intHex, + intOct: int.intOct, + intTime: timestamp.intTime, + map: map.map, + merge: merge.merge, + null: _null.nullTag, + omap: omap.omap, + pairs: pairs.pairs, + seq: seq.seq, + set: set.set, + timestamp: timestamp.timestamp + }; + var coreKnownTags = { + "tag:yaml.org,2002:binary": binary.binary, + "tag:yaml.org,2002:merge": merge.merge, + "tag:yaml.org,2002:omap": omap.omap, + "tag:yaml.org,2002:pairs": pairs.pairs, + "tag:yaml.org,2002:set": set.set, + "tag:yaml.org,2002:timestamp": timestamp.timestamp + }; + function getTags(customTags, schemaName, addMergeTag) { + const schemaTags = schemas.get(schemaName); + if (schemaTags && !customTags) { + return addMergeTag && !schemaTags.includes(merge.merge) ? schemaTags.concat(merge.merge) : schemaTags.slice(); + } + let tags = schemaTags; + if (!tags) { + if (Array.isArray(customTags)) + tags = []; + else { + const keys = Array.from(schemas.keys()).filter((key) => key !== "yaml11").map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown schema "${schemaName}"; use one of ${keys} or define customTags array`); + } + } + if (Array.isArray(customTags)) { + for (const tag of customTags) + tags = tags.concat(tag); + } else if (typeof customTags === "function") { + tags = customTags(tags.slice()); + } + if (addMergeTag) + tags = tags.concat(merge.merge); + return tags.reduce((tags2, tag) => { + const tagObj = typeof tag === "string" ? tagsByName[tag] : tag; + if (!tagObj) { + const tagName = JSON.stringify(tag); + const keys = Object.keys(tagsByName).map((key) => JSON.stringify(key)).join(", "); + throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`); + } + if (!tags2.includes(tagObj)) + tags2.push(tagObj); + return tags2; + }, []); + } + exports.coreKnownTags = coreKnownTags; + exports.getTags = getTags; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js +var require_Schema = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/schema/Schema.js"(exports) { + "use strict"; + var identity = require_identity(); + var map = require_map(); + var seq = require_seq(); + var string = require_string(); + var tags = require_tags(); + var sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0; + var Schema = class _Schema { + constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) { + this.compat = Array.isArray(compat) ? tags.getTags(compat, "compat") : compat ? tags.getTags(null, compat) : null; + this.name = typeof schema === "string" && schema || "core"; + this.knownTags = resolveKnownTags ? tags.coreKnownTags : {}; + this.tags = tags.getTags(customTags, this.name, merge); + this.toStringOptions = toStringDefaults ?? null; + Object.defineProperty(this, identity.MAP, { value: map.map }); + Object.defineProperty(this, identity.SCALAR, { value: string.string }); + Object.defineProperty(this, identity.SEQ, { value: seq.seq }); + this.sortMapEntries = typeof sortMapEntries === "function" ? sortMapEntries : sortMapEntries === true ? sortMapEntriesByKey : null; + } + clone() { + const copy = Object.create(_Schema.prototype, Object.getOwnPropertyDescriptors(this)); + copy.tags = this.tags.slice(); + return copy; + } + }; + exports.Schema = Schema; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js +var require_stringifyDocument = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) { + "use strict"; + var identity = require_identity(); + var stringify3 = require_stringify(); + var stringifyComment = require_stringifyComment(); + function stringifyDocument(doc, options) { + const lines = []; + let hasDirectives = options.directives === true; + if (options.directives !== false && doc.directives) { + const dir = doc.directives.toString(doc); + if (dir) { + lines.push(dir); + hasDirectives = true; + } else if (doc.directives.docStart) + hasDirectives = true; + } + if (hasDirectives) + lines.push("---"); + const ctx = stringify3.createStringifyContext(doc, options); + const { commentString } = ctx.options; + if (doc.commentBefore) { + if (lines.length !== 1) + lines.unshift(""); + const cs = commentString(doc.commentBefore); + lines.unshift(stringifyComment.indentComment(cs, "")); + } + let chompKeep = false; + let contentComment = null; + if (doc.contents) { + if (identity.isNode(doc.contents)) { + if (doc.contents.spaceBefore && hasDirectives) + lines.push(""); + if (doc.contents.commentBefore) { + const cs = commentString(doc.contents.commentBefore); + lines.push(stringifyComment.indentComment(cs, "")); + } + ctx.forceBlockIndent = !!doc.comment; + contentComment = doc.contents.comment; + } + const onChompKeep = contentComment ? void 0 : () => chompKeep = true; + let body = stringify3.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep); + if (contentComment) + body += stringifyComment.lineComment(body, "", commentString(contentComment)); + if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") { + lines[lines.length - 1] = `--- ${body}`; + } else + lines.push(body); + } else { + lines.push(stringify3.stringify(doc.contents, ctx)); + } + if (doc.directives?.docEnd) { + if (doc.comment) { + const cs = commentString(doc.comment); + if (cs.includes("\n")) { + lines.push("..."); + lines.push(stringifyComment.indentComment(cs, "")); + } else { + lines.push(`... ${cs}`); + } + } else { + lines.push("..."); + } + } else { + let dc = doc.comment; + if (dc && chompKeep) + dc = dc.replace(/^\n+/, ""); + if (dc) { + if ((!chompKeep || contentComment) && lines[lines.length - 1] !== "") + lines.push(""); + lines.push(stringifyComment.indentComment(commentString(dc), "")); + } + } + return lines.join("\n") + "\n"; + } + exports.stringifyDocument = stringifyDocument; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js +var require_Document = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/doc/Document.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var Collection = require_Collection(); + var identity = require_identity(); + var Pair = require_Pair(); + var toJS = require_toJS(); + var Schema = require_Schema(); + var stringifyDocument = require_stringifyDocument(); + var anchors = require_anchors(); + var applyReviver = require_applyReviver(); + var createNode = require_createNode(); + var directives = require_directives(); + var Document2 = class _Document { + constructor(value, replacer, options) { + this.commentBefore = null; + this.comment = null; + this.errors = []; + this.warnings = []; + Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC }); + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const opt = Object.assign({ + intAsBigInt: false, + keepSourceTokens: false, + logLevel: "warn", + prettyErrors: true, + strict: true, + stringKeys: false, + uniqueKeys: true, + version: "1.2" + }, options); + this.options = opt; + let { version } = opt; + if (options?._directives) { + this.directives = options._directives.atDocument(); + if (this.directives.yaml.explicit) + version = this.directives.yaml.version; + } else + this.directives = new directives.Directives({ version }); + this.setSchema(version, options); + this.contents = value === void 0 ? null : this.createNode(value, _replacer, options); + } + /** + * Create a deep copy of this Document and its contents. + * + * Custom Node values that inherit from `Object` still refer to their original instances. + */ + clone() { + const copy = Object.create(_Document.prototype, { + [identity.NODE_TYPE]: { value: identity.DOC } + }); + copy.commentBefore = this.commentBefore; + copy.comment = this.comment; + copy.errors = this.errors.slice(); + copy.warnings = this.warnings.slice(); + copy.options = Object.assign({}, this.options); + if (this.directives) + copy.directives = this.directives.clone(); + copy.schema = this.schema.clone(); + copy.contents = identity.isNode(this.contents) ? this.contents.clone(copy.schema) : this.contents; + if (this.range) + copy.range = this.range.slice(); + return copy; + } + /** Adds a value to the document. */ + add(value) { + if (assertCollection(this.contents)) + this.contents.add(value); + } + /** Adds a value to the document. */ + addIn(path14, value) { + if (assertCollection(this.contents)) + this.contents.addIn(path14, value); + } + /** + * Create a new `Alias` node, ensuring that the target `node` has the required anchor. + * + * If `node` already has an anchor, `name` is ignored. + * Otherwise, the `node.anchor` value will be set to `name`, + * or if an anchor with that name is already present in the document, + * `name` will be used as a prefix for a new unique anchor. + * If `name` is undefined, the generated anchor will use 'a' as a prefix. + */ + createAlias(node, name) { + if (!node.anchor) { + const prev = anchors.anchorNames(this); + node.anchor = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + !name || prev.has(name) ? anchors.findNewAnchor(name || "a", prev) : name; + } + return new Alias.Alias(node.anchor); + } + createNode(value, replacer, options) { + let _replacer = void 0; + if (typeof replacer === "function") { + value = replacer.call({ "": value }, "", value); + _replacer = replacer; + } else if (Array.isArray(replacer)) { + const keyToStr = (v) => typeof v === "number" || v instanceof String || v instanceof Number; + const asStr = replacer.filter(keyToStr).map(String); + if (asStr.length > 0) + replacer = replacer.concat(asStr); + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + replacer = void 0; + } + const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {}; + const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors( + this, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + anchorPrefix || "a" + ); + const ctx = { + aliasDuplicateObjects: aliasDuplicateObjects ?? true, + keepUndefined: keepUndefined ?? false, + onAnchor, + onTagObj, + replacer: _replacer, + schema: this.schema, + sourceObjects + }; + const node = createNode.createNode(value, tag, ctx); + if (flow && identity.isCollection(node)) + node.flow = true; + setAnchors(); + return node; + } + /** + * Convert a key and a value into a `Pair` using the current schema, + * recursively wrapping all values as `Scalar` or `Collection` nodes. + */ + createPair(key, value, options = {}) { + const k = this.createNode(key, null, options); + const v = this.createNode(value, null, options); + return new Pair.Pair(k, v); + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + delete(key) { + return assertCollection(this.contents) ? this.contents.delete(key) : false; + } + /** + * Removes a value from the document. + * @returns `true` if the item was found and removed. + */ + deleteIn(path14) { + if (Collection.isEmptyPath(path14)) { + if (this.contents == null) + return false; + this.contents = null; + return true; + } + return assertCollection(this.contents) ? this.contents.deleteIn(path14) : false; + } + /** + * Returns item at `key`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + get(key, keepScalar) { + return identity.isCollection(this.contents) ? this.contents.get(key, keepScalar) : void 0; + } + /** + * Returns item at `path`, or `undefined` if not found. By default unwraps + * scalar values from their surrounding node; to disable set `keepScalar` to + * `true` (collections are always returned intact). + */ + getIn(path14, keepScalar) { + if (Collection.isEmptyPath(path14)) + return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents; + return identity.isCollection(this.contents) ? this.contents.getIn(path14, keepScalar) : void 0; + } + /** + * Checks if the document includes a value with the key `key`. + */ + has(key) { + return identity.isCollection(this.contents) ? this.contents.has(key) : false; + } + /** + * Checks if the document includes a value at `path`. + */ + hasIn(path14) { + if (Collection.isEmptyPath(path14)) + return this.contents !== void 0; + return identity.isCollection(this.contents) ? this.contents.hasIn(path14) : false; + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + set(key, value) { + if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, [key], value); + } else if (assertCollection(this.contents)) { + this.contents.set(key, value); + } + } + /** + * Sets a value in this document. For `!!set`, `value` needs to be a + * boolean to add/remove the item from the set. + */ + setIn(path14, value) { + if (Collection.isEmptyPath(path14)) { + this.contents = value; + } else if (this.contents == null) { + this.contents = Collection.collectionFromPath(this.schema, Array.from(path14), value); + } else if (assertCollection(this.contents)) { + this.contents.setIn(path14, value); + } + } + /** + * Change the YAML version and schema used by the document. + * A `null` version disables support for directives, explicit tags, anchors, and aliases. + * It also requires the `schema` option to be given as a `Schema` instance value. + * + * Overrides all previously set schema options. + */ + setSchema(version, options = {}) { + if (typeof version === "number") + version = String(version); + let opt; + switch (version) { + case "1.1": + if (this.directives) + this.directives.yaml.version = "1.1"; + else + this.directives = new directives.Directives({ version: "1.1" }); + opt = { resolveKnownTags: false, schema: "yaml-1.1" }; + break; + case "1.2": + case "next": + if (this.directives) + this.directives.yaml.version = version; + else + this.directives = new directives.Directives({ version }); + opt = { resolveKnownTags: true, schema: "core" }; + break; + case null: + if (this.directives) + delete this.directives; + opt = null; + break; + default: { + const sv = JSON.stringify(version); + throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`); + } + } + if (options.schema instanceof Object) + this.schema = options.schema; + else if (opt) + this.schema = new Schema.Schema(Object.assign(opt, options)); + else + throw new Error(`With a null YAML version, the { schema: Schema } option is required`); + } + // json & jsonArg are only used from toJSON() + toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) { + const ctx = { + anchors: /* @__PURE__ */ new Map(), + doc: this, + keep: !json, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === "number" ? maxAliasCount : 100 + }; + const res = toJS.toJS(this.contents, jsonArg ?? "", ctx); + if (typeof onAnchor === "function") + for (const { count, res: res2 } of ctx.anchors.values()) + onAnchor(res2, count); + return typeof reviver === "function" ? applyReviver.applyReviver(reviver, { "": res }, "", res) : res; + } + /** + * A JSON representation of the document `contents`. + * + * @param jsonArg Used by `JSON.stringify` to indicate the array index or + * property name. + */ + toJSON(jsonArg, onAnchor) { + return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor }); + } + /** A YAML representation of the document. */ + toString(options = {}) { + if (this.errors.length > 0) + throw new Error("Document with errors cannot be stringified"); + if ("indent" in options && (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) { + const s = JSON.stringify(options.indent); + throw new Error(`"indent" option must be a positive integer, not ${s}`); + } + return stringifyDocument.stringifyDocument(this, options); + } + }; + function assertCollection(contents) { + if (identity.isCollection(contents)) + return true; + throw new Error("Expected a YAML collection as document contents"); + } + exports.Document = Document2; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js +var require_errors = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/errors.js"(exports) { + "use strict"; + var YAMLError = class extends Error { + constructor(name, pos, code, message) { + super(); + this.name = name; + this.code = code; + this.message = message; + this.pos = pos; + } + }; + var YAMLParseError = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLParseError", pos, code, message); + } + }; + var YAMLWarning = class extends YAMLError { + constructor(pos, code, message) { + super("YAMLWarning", pos, code, message); + } + }; + var prettifyError = (src, lc) => (error) => { + if (error.pos[0] === -1) + return; + error.linePos = error.pos.map((pos) => lc.linePos(pos)); + const { line, col } = error.linePos[0]; + error.message += ` at line ${line}, column ${col}`; + let ci = col - 1; + let lineStr = src.substring(lc.lineStarts[line - 1], lc.lineStarts[line]).replace(/[\n\r]+$/, ""); + if (ci >= 60 && lineStr.length > 80) { + const trimStart = Math.min(ci - 39, lineStr.length - 79); + lineStr = "…" + lineStr.substring(trimStart); + ci -= trimStart - 1; + } + if (lineStr.length > 80) + lineStr = lineStr.substring(0, 79) + "…"; + if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) { + let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]); + if (prev.length > 80) + prev = prev.substring(0, 79) + "…\n"; + lineStr = prev + lineStr; + } + if (/[^ ]/.test(lineStr)) { + let count = 1; + const end = error.linePos[1]; + if (end?.line === line && end.col > col) { + count = Math.max(1, Math.min(end.col - col, 80 - ci)); + } + const pointer = " ".repeat(ci) + "^".repeat(count); + error.message += `: + +${lineStr} +${pointer} +`; + } + }; + exports.YAMLError = YAMLError; + exports.YAMLParseError = YAMLParseError; + exports.YAMLWarning = YAMLWarning; + exports.prettifyError = prettifyError; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js +var require_resolve_props = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-props.js"(exports) { + "use strict"; + function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) { + let spaceBefore = false; + let atNewline = startOnNewline; + let hasSpace = startOnNewline; + let comment = ""; + let commentSep = ""; + let hasNewline = false; + let reqSpace = false; + let tab = null; + let anchor = null; + let tag = null; + let newlineAfterProp = null; + let comma = null; + let found = null; + let start = null; + for (const token of tokens) { + if (reqSpace) { + if (token.type !== "space" && token.type !== "newline" && token.type !== "comma") + onError(token.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + reqSpace = false; + } + if (tab) { + if (atNewline && token.type !== "comment" && token.type !== "newline") { + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + } + tab = null; + } + switch (token.type) { + case "space": + if (!flow && (indicator !== "doc-start" || next?.type !== "flow-collection") && token.source.includes(" ")) { + tab = token; + } + hasSpace = true; + break; + case "comment": { + if (!hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = token.source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += commentSep + cb; + commentSep = ""; + atNewline = false; + break; + } + case "newline": + if (atNewline) { + if (comment) + comment += token.source; + else if (!found || indicator !== "seq-item-ind") + spaceBefore = true; + } else + commentSep += token.source; + atNewline = true; + hasNewline = true; + if (anchor || tag) + newlineAfterProp = token; + hasSpace = true; + break; + case "anchor": + if (anchor) + onError(token, "MULTIPLE_ANCHORS", "A node can have at most one anchor"); + if (token.source.endsWith(":")) + onError(token.offset + token.source.length - 1, "BAD_ALIAS", "Anchor ending in : is ambiguous", true); + anchor = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + case "tag": { + if (tag) + onError(token, "MULTIPLE_TAGS", "A node can have at most one tag"); + tag = token; + start ?? (start = token.offset); + atNewline = false; + hasSpace = false; + reqSpace = true; + break; + } + case indicator: + if (anchor || tag) + onError(token, "BAD_PROP_ORDER", `Anchors and tags must be after the ${token.source} indicator`); + if (found) + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.source} in ${flow ?? "collection"}`); + found = token; + atNewline = indicator === "seq-item-ind" || indicator === "explicit-key-ind"; + hasSpace = false; + break; + case "comma": + if (flow) { + if (comma) + onError(token, "UNEXPECTED_TOKEN", `Unexpected , in ${flow}`); + comma = token; + atNewline = false; + hasSpace = false; + break; + } + // else fallthrough + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${token.type} token`); + atNewline = false; + hasSpace = false; + } + } + const last = tokens[tokens.length - 1]; + const end = last ? last.offset + last.source.length : offset; + if (reqSpace && next && next.type !== "space" && next.type !== "newline" && next.type !== "comma" && (next.type !== "scalar" || next.source !== "")) { + onError(next.offset, "MISSING_CHAR", "Tags and anchors must be separated from the next token by white space"); + } + if (tab && (atNewline && tab.indent <= parentIndent || next?.type === "block-map" || next?.type === "block-seq")) + onError(tab, "TAB_AS_INDENT", "Tabs are not allowed as indentation"); + return { + comma, + found, + spaceBefore, + comment, + hasNewline, + anchor, + tag, + newlineAfterProp, + end, + start: start ?? end + }; + } + exports.resolveProps = resolveProps; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js +var require_util_contains_newline = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-contains-newline.js"(exports) { + "use strict"; + function containsNewline(key) { + if (!key) + return null; + switch (key.type) { + case "alias": + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + if (key.source.includes("\n")) + return true; + if (key.end) { + for (const st of key.end) + if (st.type === "newline") + return true; + } + return false; + case "flow-collection": + for (const it of key.items) { + for (const st of it.start) + if (st.type === "newline") + return true; + if (it.sep) { + for (const st of it.sep) + if (st.type === "newline") + return true; + } + if (containsNewline(it.key) || containsNewline(it.value)) + return true; + } + return false; + default: + return true; + } + } + exports.containsNewline = containsNewline; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js +var require_util_flow_indent_check = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-flow-indent-check.js"(exports) { + "use strict"; + var utilContainsNewline = require_util_contains_newline(); + function flowIndentCheck(indent, fc, onError) { + if (fc?.type === "flow-collection") { + const end = fc.end[0]; + if (end.indent === indent && (end.source === "]" || end.source === "}") && utilContainsNewline.containsNewline(fc)) { + const msg = "Flow end indicator should be more indented than parent"; + onError(end, "BAD_INDENT", msg, true); + } + } + } + exports.flowIndentCheck = flowIndentCheck; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js +var require_util_map_includes = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-map-includes.js"(exports) { + "use strict"; + var identity = require_identity(); + function mapIncludes(ctx, items, search) { + const { uniqueKeys } = ctx.options; + if (uniqueKeys === false) + return false; + const isEqual = typeof uniqueKeys === "function" ? uniqueKeys : (a, b) => a === b || identity.isScalar(a) && identity.isScalar(b) && a.value === b.value; + return items.some((pair) => isEqual(pair.key, search)); + } + exports.mapIncludes = mapIncludes; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js +var require_resolve_block_map = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-map.js"(exports) { + "use strict"; + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + var utilMapIncludes = require_util_map_includes(); + var startColMsg = "All mapping items must start at the same column"; + function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap; + const map = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + let offset = bm.offset; + let commentEnd = null; + for (const collItem of bm.items) { + const { start, key, sep, value } = collItem; + const keyProps = resolveProps.resolveProps(start, { + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: bm.indent, + startOnNewline: true + }); + const implicitKey = !keyProps.found; + if (implicitKey) { + if (key) { + if (key.type === "block-seq") + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "A block sequence may not be used as an implicit map key"); + else if ("indent" in key && key.indent !== bm.indent) + onError(offset, "BAD_INDENT", startColMsg); + } + if (!keyProps.anchor && !keyProps.tag && !sep) { + commentEnd = keyProps.end; + if (keyProps.comment) { + if (map.comment) + map.comment += "\n" + keyProps.comment; + else + map.comment = keyProps.comment; + } + continue; + } + if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) { + onError(key ?? start[start.length - 1], "MULTILINE_IMPLICIT_KEY", "Implicit keys need to be on a single line"); + } + } else if (keyProps.found?.indent !== bm.indent) { + onError(offset, "BAD_INDENT", startColMsg); + } + ctx.atKey = true; + const keyStart = keyProps.end; + const keyNode = key ? composeNode(ctx, key, keyProps, onError) : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError); + ctx.atKey = false; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + const valueProps = resolveProps.resolveProps(sep ?? [], { + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: bm.indent, + startOnNewline: !key || key.type === "block-scalar" + }); + offset = valueProps.end; + if (valueProps.found) { + if (implicitKey) { + if (value?.type === "block-map" && !valueProps.hasNewline) + onError(offset, "BLOCK_AS_IMPLICIT_KEY", "Nested mappings are not allowed in compact mappings"); + if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024) + onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key"); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep, null, valueProps, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError); + offset = valueNode.range[2]; + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } else { + if (implicitKey) + onError(keyNode.range, "MISSING_CHAR", "Implicit map keys need to be followed by map values"); + if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + map.items.push(pair); + } + } + if (commentEnd && commentEnd < offset) + onError(commentEnd, "IMPOSSIBLE", "Map comment with trailing content"); + map.range = [bm.offset, offset, commentEnd ?? offset]; + return map; + } + exports.resolveBlockMap = resolveBlockMap; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js +var require_resolve_block_seq = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-seq.js"(exports) { + "use strict"; + var YAMLSeq = require_YAMLSeq(); + var resolveProps = require_resolve_props(); + var utilFlowIndentCheck = require_util_flow_indent_check(); + function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) { + const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq; + const seq = new NodeClass(ctx.schema); + if (ctx.atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = bs.offset; + let commentEnd = null; + for (const { start, value } of bs.items) { + const props = resolveProps.resolveProps(start, { + indicator: "seq-item-ind", + next: value, + offset, + onError, + parentIndent: bs.indent, + startOnNewline: true + }); + if (!props.found) { + if (props.anchor || props.tag || value) { + if (value?.type === "block-seq") + onError(props.end, "BAD_INDENT", "All sequence items must start at the same column"); + else + onError(offset, "MISSING_CHAR", "Sequence item without - indicator"); + } else { + commentEnd = props.end; + if (props.comment) + seq.comment = props.comment; + continue; + } + } + const node = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, start, null, props, onError); + if (ctx.schema.compat) + utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError); + offset = node.range[2]; + seq.items.push(node); + } + seq.range = [bs.offset, offset, commentEnd ?? offset]; + return seq; + } + exports.resolveBlockSeq = resolveBlockSeq; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js +var require_resolve_end = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-end.js"(exports) { + "use strict"; + function resolveEnd(end, offset, reqSpace, onError) { + let comment = ""; + if (end) { + let hasSpace = false; + let sep = ""; + for (const token of end) { + const { source, type } = token; + switch (type) { + case "space": + hasSpace = true; + break; + case "comment": { + if (reqSpace && !hasSpace) + onError(token, "MISSING_CHAR", "Comments must be separated from other tokens by white space characters"); + const cb = source.substring(1) || " "; + if (!comment) + comment = cb; + else + comment += sep + cb; + sep = ""; + break; + } + case "newline": + if (comment) + sep += source; + hasSpace = true; + break; + default: + onError(token, "UNEXPECTED_TOKEN", `Unexpected ${type} at node end`); + } + offset += source.length; + } + } + return { comment, offset }; + } + exports.resolveEnd = resolveEnd; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js +var require_resolve_flow_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Pair = require_Pair(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + var utilContainsNewline = require_util_contains_newline(); + var utilMapIncludes = require_util_map_includes(); + var blockMsg = "Block collections are not allowed within flow collections"; + var isBlock = (token) => token && (token.type === "block-map" || token.type === "block-seq"); + function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) { + const isMap = fc.start.source === "{"; + const fcName = isMap ? "flow map" : "flow sequence"; + const NodeClass = tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq); + const coll = new NodeClass(ctx.schema); + coll.flow = true; + const atRoot = ctx.atRoot; + if (atRoot) + ctx.atRoot = false; + if (ctx.atKey) + ctx.atKey = false; + let offset = fc.offset + fc.start.source.length; + for (let i = 0; i < fc.items.length; ++i) { + const collItem = fc.items[i]; + const { start, key, sep, value } = collItem; + const props = resolveProps.resolveProps(start, { + flow: fcName, + indicator: "explicit-key-ind", + next: key ?? sep?.[0], + offset, + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (!props.found) { + if (!props.anchor && !props.tag && !sep && !value) { + if (i === 0 && props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + else if (i < fc.items.length - 1) + onError(props.start, "UNEXPECTED_TOKEN", `Unexpected empty item in ${fcName}`); + if (props.comment) { + if (coll.comment) + coll.comment += "\n" + props.comment; + else + coll.comment = props.comment; + } + offset = props.end; + continue; + } + if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key)) + onError( + key, + // checked by containsNewline() + "MULTILINE_IMPLICIT_KEY", + "Implicit keys of flow sequence pairs need to be on a single line" + ); + } + if (i === 0) { + if (props.comma) + onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`); + } else { + if (!props.comma) + onError(props.start, "MISSING_CHAR", `Missing , between ${fcName} items`); + if (props.comment) { + let prevItemComment = ""; + loop: for (const st of start) { + switch (st.type) { + case "comma": + case "space": + break; + case "comment": + prevItemComment = st.source.substring(1); + break loop; + default: + break loop; + } + } + if (prevItemComment) { + let prev = coll.items[coll.items.length - 1]; + if (identity.isPair(prev)) + prev = prev.value ?? prev.key; + if (prev.comment) + prev.comment += "\n" + prevItemComment; + else + prev.comment = prevItemComment; + props.comment = props.comment.substring(prevItemComment.length + 1); + } + } + } + if (!isMap && !sep && !props.found) { + const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep, null, props, onError); + coll.items.push(valueNode); + offset = valueNode.range[2]; + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else { + ctx.atKey = true; + const keyStart = props.end; + const keyNode = key ? composeNode(ctx, key, props, onError) : composeEmptyNode(ctx, keyStart, start, null, props, onError); + if (isBlock(key)) + onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg); + ctx.atKey = false; + const valueProps = resolveProps.resolveProps(sep ?? [], { + flow: fcName, + indicator: "map-value-ind", + next: value, + offset: keyNode.range[2], + onError, + parentIndent: fc.indent, + startOnNewline: false + }); + if (valueProps.found) { + if (!isMap && !props.found && ctx.options.strict) { + if (sep) + for (const st of sep) { + if (st === valueProps.found) + break; + if (st.type === "newline") { + onError(st, "MULTILINE_IMPLICIT_KEY", "Implicit keys of flow sequence pairs need to be on a single line"); + break; + } + } + if (props.start < valueProps.found.offset - 1024) + onError(valueProps.found, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit flow sequence key"); + } + } else if (value) { + if ("source" in value && value.source?.[0] === ":") + onError(value, "MISSING_CHAR", `Missing space after : in ${fcName}`); + else + onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`); + } + const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError) : null; + if (valueNode) { + if (isBlock(value)) + onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg); + } else if (valueProps.comment) { + if (keyNode.comment) + keyNode.comment += "\n" + valueProps.comment; + else + keyNode.comment = valueProps.comment; + } + const pair = new Pair.Pair(keyNode, valueNode); + if (ctx.options.keepSourceTokens) + pair.srcToken = collItem; + if (isMap) { + const map = coll; + if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode)) + onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique"); + map.items.push(pair); + } else { + const map = new YAMLMap.YAMLMap(ctx.schema); + map.flow = true; + map.items.push(pair); + const endRange = (valueNode ?? keyNode).range; + map.range = [keyNode.range[0], endRange[1], endRange[2]]; + coll.items.push(map); + } + offset = valueNode ? valueNode.range[2] : valueProps.end; + } + } + const expectedEnd = isMap ? "}" : "]"; + const [ce, ...ee] = fc.end; + let cePos = offset; + if (ce?.source === expectedEnd) + cePos = ce.offset + ce.source.length; + else { + const name = fcName[0].toUpperCase() + fcName.substring(1); + const msg = atRoot ? `${name} must end with a ${expectedEnd}` : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`; + onError(offset, atRoot ? "MISSING_CHAR" : "BAD_INDENT", msg); + if (ce && ce.source.length !== 1) + ee.unshift(ce); + } + if (ee.length > 0) { + const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError); + if (end.comment) { + if (coll.comment) + coll.comment += "\n" + end.comment; + else + coll.comment = end.comment; + } + coll.range = [fc.offset, cePos, end.offset]; + } else { + coll.range = [fc.offset, cePos, cePos]; + } + return coll; + } + exports.resolveFlowCollection = resolveFlowCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js +var require_compose_collection = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-collection.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var resolveBlockMap = require_resolve_block_map(); + var resolveBlockSeq = require_resolve_block_seq(); + var resolveFlowCollection = require_resolve_flow_collection(); + function resolveCollection(CN, ctx, token, onError, tagName, tag) { + const coll = token.type === "block-map" ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag) : token.type === "block-seq" ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag) : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag); + const Coll = coll.constructor; + if (tagName === "!" || tagName === Coll.tagName) { + coll.tag = Coll.tagName; + return coll; + } + if (tagName) + coll.tag = tagName; + return coll; + } + function composeCollection(CN, ctx, token, props, onError) { + const tagToken = props.tag; + const tagName = !tagToken ? null : ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)); + if (token.type === "block-seq") { + const { anchor, newlineAfterProp: nl } = props; + const lastProp = anchor && tagToken ? anchor.offset > tagToken.offset ? anchor : tagToken : anchor ?? tagToken; + if (lastProp && (!nl || nl.offset < lastProp.offset)) { + const message = "Missing newline after block sequence props"; + onError(lastProp, "MISSING_CHAR", message); + } + } + const expType = token.type === "block-map" ? "map" : token.type === "block-seq" ? "seq" : token.start.source === "{" ? "map" : "seq"; + if (!tagToken || !tagName || tagName === "!" || tagName === YAMLMap.YAMLMap.tagName && expType === "map" || tagName === YAMLSeq.YAMLSeq.tagName && expType === "seq") { + return resolveCollection(CN, ctx, token, onError, tagName); + } + let tag = ctx.schema.tags.find((t) => t.tag === tagName && t.collection === expType); + if (!tag) { + const kt = ctx.schema.knownTags[tagName]; + if (kt?.collection === expType) { + ctx.schema.tags.push(Object.assign({}, kt, { default: false })); + tag = kt; + } else { + if (kt) { + onError(tagToken, "BAD_COLLECTION_TYPE", `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? "scalar"}`, true); + } else { + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, true); + } + return resolveCollection(CN, ctx, token, onError, tagName); + } + } + const coll = resolveCollection(CN, ctx, token, onError, tagName, tag); + const res = tag.resolve?.(coll, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg), ctx.options) ?? coll; + const node = identity.isNode(res) ? res : new Scalar.Scalar(res); + node.range = coll.range; + node.tag = tagName; + if (tag?.format) + node.format = tag.format; + return node; + } + exports.composeCollection = composeCollection; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js +var require_resolve_block_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-block-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + function resolveBlockScalar(ctx, scalar, onError) { + const start = scalar.offset; + const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError); + if (!header) + return { value: "", type: null, comment: "", range: [start, start, start] }; + const type = header.mode === ">" ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL; + const lines = scalar.source ? splitLines(scalar.source) : []; + let chompStart = lines.length; + for (let i = lines.length - 1; i >= 0; --i) { + const content = lines[i][1]; + if (content === "" || content === "\r") + chompStart = i; + else + break; + } + if (chompStart === 0) { + const value2 = header.chomp === "+" && lines.length > 0 ? "\n".repeat(Math.max(1, lines.length - 1)) : ""; + let end2 = start + header.length; + if (scalar.source) + end2 += scalar.source.length; + return { value: value2, type, comment: header.comment, range: [start, end2, end2] }; + } + let trimIndent = scalar.indent + header.indent; + let offset = scalar.offset + header.length; + let contentStart = 0; + for (let i = 0; i < chompStart; ++i) { + const [indent, content] = lines[i]; + if (content === "" || content === "\r") { + if (header.indent === 0 && indent.length > trimIndent) + trimIndent = indent.length; + } else { + if (indent.length < trimIndent) { + const message = "Block scalars with more-indented leading empty lines must use an explicit indentation indicator"; + onError(offset + indent.length, "MISSING_CHAR", message); + } + if (header.indent === 0) + trimIndent = indent.length; + contentStart = i; + if (trimIndent === 0 && !ctx.atRoot) { + const message = "Block scalar values in collections must be indented"; + onError(offset, "BAD_INDENT", message); + } + break; + } + offset += indent.length + content.length + 1; + } + for (let i = lines.length - 1; i >= chompStart; --i) { + if (lines[i][0].length > trimIndent) + chompStart = i + 1; + } + let value = ""; + let sep = ""; + let prevMoreIndented = false; + for (let i = 0; i < contentStart; ++i) + value += lines[i][0].slice(trimIndent) + "\n"; + for (let i = contentStart; i < chompStart; ++i) { + let [indent, content] = lines[i]; + offset += indent.length + content.length + 1; + const crlf = content[content.length - 1] === "\r"; + if (crlf) + content = content.slice(0, -1); + if (content && indent.length < trimIndent) { + const src = header.indent ? "explicit indentation indicator" : "first line"; + const message = `Block scalar lines must not be less indented than their ${src}`; + onError(offset - content.length - (crlf ? 2 : 1), "BAD_INDENT", message); + indent = ""; + } + if (type === Scalar.Scalar.BLOCK_LITERAL) { + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + } else if (indent.length > trimIndent || content[0] === " ") { + if (sep === " ") + sep = "\n"; + else if (!prevMoreIndented && sep === "\n") + sep = "\n\n"; + value += sep + indent.slice(trimIndent) + content; + sep = "\n"; + prevMoreIndented = true; + } else if (content === "") { + if (sep === "\n") + value += "\n"; + else + sep = "\n"; + } else { + value += sep + content; + sep = " "; + prevMoreIndented = false; + } + } + switch (header.chomp) { + case "-": + break; + case "+": + for (let i = chompStart; i < lines.length; ++i) + value += "\n" + lines[i][0].slice(trimIndent); + if (value[value.length - 1] !== "\n") + value += "\n"; + break; + default: + value += "\n"; + } + const end = start + header.length + scalar.source.length; + return { value, type, comment: header.comment, range: [start, end, end] }; + } + function parseBlockScalarHeader({ offset, props }, strict, onError) { + if (props[0].type !== "block-scalar-header") { + onError(props[0], "IMPOSSIBLE", "Block scalar header not found"); + return null; + } + const { source } = props[0]; + const mode = source[0]; + let indent = 0; + let chomp = ""; + let error = -1; + for (let i = 1; i < source.length; ++i) { + const ch = source[i]; + if (!chomp && (ch === "-" || ch === "+")) + chomp = ch; + else { + const n = Number(ch); + if (!indent && n) + indent = n; + else if (error === -1) + error = offset + i; + } + } + if (error !== -1) + onError(error, "UNEXPECTED_TOKEN", `Block scalar header includes extra characters: ${source}`); + let hasSpace = false; + let comment = ""; + let length = source.length; + for (let i = 1; i < props.length; ++i) { + const token = props[i]; + switch (token.type) { + case "space": + hasSpace = true; + // fallthrough + case "newline": + length += token.source.length; + break; + case "comment": + if (strict && !hasSpace) { + const message = "Comments must be separated from other tokens by white space characters"; + onError(token, "MISSING_CHAR", message); + } + length += token.source.length; + comment = token.source.substring(1); + break; + case "error": + onError(token, "UNEXPECTED_TOKEN", token.message); + length += token.source.length; + break; + /* istanbul ignore next should not happen */ + default: { + const message = `Unexpected token in block scalar header: ${token.type}`; + onError(token, "UNEXPECTED_TOKEN", message); + const ts = token.source; + if (ts && typeof ts === "string") + length += ts.length; + } + } + } + return { mode, indent, chomp, comment, length }; + } + function splitLines(source) { + const split = source.split(/\n( *)/); + const first = split[0]; + const m = first.match(/^( *)/); + const line0 = m?.[1] ? [m[1], first.slice(m[1].length)] : ["", first]; + const lines = [line0]; + for (let i = 1; i < split.length; i += 2) + lines.push([split[i], split[i + 1]]); + return lines; + } + exports.resolveBlockScalar = resolveBlockScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js +var require_resolve_flow_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/resolve-flow-scalar.js"(exports) { + "use strict"; + var Scalar = require_Scalar(); + var resolveEnd = require_resolve_end(); + function resolveFlowScalar(scalar, strict, onError) { + const { offset, type, source, end } = scalar; + let _type; + let value; + const _onError = (rel, code, msg) => onError(offset + rel, code, msg); + switch (type) { + case "scalar": + _type = Scalar.Scalar.PLAIN; + value = plainValue(source, _onError); + break; + case "single-quoted-scalar": + _type = Scalar.Scalar.QUOTE_SINGLE; + value = singleQuotedValue(source, _onError); + break; + case "double-quoted-scalar": + _type = Scalar.Scalar.QUOTE_DOUBLE; + value = doubleQuotedValue(source, _onError); + break; + /* istanbul ignore next should not happen */ + default: + onError(scalar, "UNEXPECTED_TOKEN", `Expected a flow scalar value, but found: ${type}`); + return { + value: "", + type: null, + comment: "", + range: [offset, offset + source.length, offset + source.length] + }; + } + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError); + return { + value, + type: _type, + comment: re.comment, + range: [offset, valueEnd, re.offset] + }; + } + function plainValue(source, onError) { + let badChar = ""; + switch (source[0]) { + /* istanbul ignore next should not happen */ + case " ": + badChar = "a tab character"; + break; + case ",": + badChar = "flow indicator character ,"; + break; + case "%": + badChar = "directive indicator character %"; + break; + case "|": + case ">": { + badChar = `block scalar indicator ${source[0]}`; + break; + } + case "@": + case "`": { + badChar = `reserved character ${source[0]}`; + break; + } + } + if (badChar) + onError(0, "BAD_SCALAR_START", `Plain value cannot start with ${badChar}`); + return foldLines(source); + } + function singleQuotedValue(source, onError) { + if (source[source.length - 1] !== "'" || source.length === 1) + onError(source.length, "MISSING_CHAR", "Missing closing 'quote"); + return foldLines(source.slice(1, -1)).replace(/''/g, "'"); + } + function foldLines(source) { + let first, line; + try { + first = new RegExp("(.*?)(? wsStart ? source.slice(wsStart, i + 1) : ch; + } else { + res += ch; + } + } + if (source[source.length - 1] !== '"' || source.length === 1) + onError(source.length, "MISSING_CHAR", 'Missing closing "quote'); + return res; + } + function foldNewline(source, offset) { + let fold = ""; + let ch = source[offset + 1]; + while (ch === " " || ch === " " || ch === "\n" || ch === "\r") { + if (ch === "\r" && source[offset + 2] !== "\n") + break; + if (ch === "\n") + fold += "\n"; + offset += 1; + ch = source[offset + 1]; + } + if (!fold) + fold = " "; + return { fold, offset }; + } + var escapeCodes = { + "0": "\0", + // null character + a: "\x07", + // bell character + b: "\b", + // backspace + e: "\x1B", + // escape character + f: "\f", + // form feed + n: "\n", + // line feed + r: "\r", + // carriage return + t: " ", + // horizontal tab + v: "\v", + // vertical tab + N: "…", + // Unicode next line + _: " ", + // Unicode non-breaking space + L: "\u2028", + // Unicode line separator + P: "\u2029", + // Unicode paragraph separator + " ": " ", + '"': '"', + "/": "/", + "\\": "\\", + " ": " " + }; + function parseCharCode(source, offset, length, onError) { + const cc = source.substr(offset, length); + const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); + const code = ok ? parseInt(cc, 16) : NaN; + try { + return String.fromCodePoint(code); + } catch { + const raw = source.substr(offset - 2, length + 2); + onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`); + return raw; + } + } + exports.resolveFlowScalar = resolveFlowScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js +var require_compose_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-scalar.js"(exports) { + "use strict"; + var identity = require_identity(); + var Scalar = require_Scalar(); + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + function composeScalar(ctx, token, tagToken, onError) { + const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError); + const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null; + let tag; + if (ctx.options.stringKeys && ctx.atKey) { + tag = ctx.schema[identity.SCALAR]; + } else if (tagName) + tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError); + else if (token.type === "scalar") + tag = findScalarTagByTest(ctx, value, token, onError); + else + tag = ctx.schema[identity.SCALAR]; + let scalar; + try { + const res = tag.resolve(value, (msg) => onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg), ctx.options); + scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res); + } catch (error) { + const msg = error instanceof Error ? error.message : String(error); + onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg); + scalar = new Scalar.Scalar(value); + } + scalar.range = range; + scalar.source = value; + if (type) + scalar.type = type; + if (tagName) + scalar.tag = tagName; + if (tag.format) + scalar.format = tag.format; + if (comment) + scalar.comment = comment; + return scalar; + } + function findScalarTagByName(schema, value, tagName, tagToken, onError) { + if (tagName === "!") + return schema[identity.SCALAR]; + const matchWithTest = []; + for (const tag of schema.tags) { + if (!tag.collection && tag.tag === tagName) { + if (tag.default && tag.test) + matchWithTest.push(tag); + else + return tag; + } + } + for (const tag of matchWithTest) + if (tag.test?.test(value)) + return tag; + const kt = schema.knownTags[tagName]; + if (kt && !kt.collection) { + schema.tags.push(Object.assign({}, kt, { default: false, test: void 0 })); + return kt; + } + onError(tagToken, "TAG_RESOLVE_FAILED", `Unresolved tag: ${tagName}`, tagName !== "tag:yaml.org,2002:str"); + return schema[identity.SCALAR]; + } + function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) { + const tag = schema.tags.find((tag2) => (tag2.default === true || atKey && tag2.default === "key") && tag2.test?.test(value)) || schema[identity.SCALAR]; + if (schema.compat) { + const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR]; + if (tag.tag !== compat.tag) { + const ts = directives.tagString(tag.tag); + const cs = directives.tagString(compat.tag); + const msg = `Value may be parsed as either ${ts} or ${cs}`; + onError(token, "TAG_RESOLVE_FAILED", msg, true); + } + } + return tag; + } + exports.composeScalar = composeScalar; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js +var require_util_empty_scalar_position = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/util-empty-scalar-position.js"(exports) { + "use strict"; + function emptyScalarPosition(offset, before, pos) { + if (before) { + pos ?? (pos = before.length); + for (let i = pos - 1; i >= 0; --i) { + let st = before[i]; + switch (st.type) { + case "space": + case "comment": + case "newline": + offset -= st.source.length; + continue; + } + st = before[++i]; + while (st?.type === "space") { + offset += st.source.length; + st = before[++i]; + } + break; + } + } + return offset; + } + exports.emptyScalarPosition = emptyScalarPosition; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js +var require_compose_node = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-node.js"(exports) { + "use strict"; + var Alias = require_Alias(); + var identity = require_identity(); + var composeCollection = require_compose_collection(); + var composeScalar = require_compose_scalar(); + var resolveEnd = require_resolve_end(); + var utilEmptyScalarPosition = require_util_empty_scalar_position(); + var CN = { composeNode, composeEmptyNode }; + function composeNode(ctx, token, props, onError) { + const atKey = ctx.atKey; + const { spaceBefore, comment, anchor, tag } = props; + let node; + let isSrcToken = true; + switch (token.type) { + case "alias": + node = composeAlias(ctx, token, onError); + if (anchor || tag) + onError(token, "ALIAS_PROPS", "An alias node must not specify any properties"); + break; + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "block-scalar": + node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + break; + case "block-map": + case "block-seq": + case "flow-collection": + try { + node = composeCollection.composeCollection(CN, ctx, token, props, onError); + if (anchor) + node.anchor = anchor.source.substring(1); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + onError(token, "RESOURCE_EXHAUSTION", message); + } + break; + default: { + const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`; + onError(token, "UNEXPECTED_TOKEN", message); + isSrcToken = false; + } + } + node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError)); + if (anchor && node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) { + const msg = "With stringKeys, all keys must be strings"; + onError(tag ?? token, "NON_STRING_KEY", msg); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + if (token.type === "scalar" && token.source === "") + node.comment = comment; + else + node.commentBefore = comment; + } + if (ctx.options.keepSourceTokens && isSrcToken) + node.srcToken = token; + return node; + } + function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) { + const token = { + type: "scalar", + offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos), + indent: -1, + source: "" + }; + const node = composeScalar.composeScalar(ctx, token, tag, onError); + if (anchor) { + node.anchor = anchor.source.substring(1); + if (node.anchor === "") + onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string"); + } + if (spaceBefore) + node.spaceBefore = true; + if (comment) { + node.comment = comment; + node.range[2] = end; + } + return node; + } + function composeAlias({ options }, { offset, source, end }, onError) { + const alias = new Alias.Alias(source.substring(1)); + if (alias.source === "") + onError(offset, "BAD_ALIAS", "Alias cannot be an empty string"); + if (alias.source.endsWith(":")) + onError(offset + source.length - 1, "BAD_ALIAS", "Alias ending in : is ambiguous", true); + const valueEnd = offset + source.length; + const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError); + alias.range = [offset, valueEnd, re.offset]; + if (re.comment) + alias.comment = re.comment; + return alias; + } + exports.composeEmptyNode = composeEmptyNode; + exports.composeNode = composeNode; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js +var require_compose_doc = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/compose-doc.js"(exports) { + "use strict"; + var Document2 = require_Document(); + var composeNode = require_compose_node(); + var resolveEnd = require_resolve_end(); + var resolveProps = require_resolve_props(); + function composeDoc(options, directives, { offset, start, value, end }, onError) { + const opts = Object.assign({ _directives: directives }, options); + const doc = new Document2.Document(void 0, opts); + const ctx = { + atKey: false, + atRoot: true, + directives: doc.directives, + options: doc.options, + schema: doc.schema + }; + const props = resolveProps.resolveProps(start, { + indicator: "doc-start", + next: value ?? end?.[0], + offset, + onError, + parentIndent: 0, + startOnNewline: true + }); + if (props.found) { + doc.directives.docStart = true; + if (value && (value.type === "block-map" || value.type === "block-seq") && !props.hasNewline) + onError(props.end, "MISSING_CHAR", "Block collection cannot start on same line with directives-end marker"); + } + doc.contents = value ? composeNode.composeNode(ctx, value, props, onError) : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError); + const contentEnd = doc.contents.range[2]; + const re = resolveEnd.resolveEnd(end, contentEnd, false, onError); + if (re.comment) + doc.comment = re.comment; + doc.range = [offset, contentEnd, re.offset]; + return doc; + } + exports.composeDoc = composeDoc; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js +var require_composer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/compose/composer.js"(exports) { + "use strict"; + var node_process = __require("process"); + var directives = require_directives(); + var Document2 = require_Document(); + var errors = require_errors(); + var identity = require_identity(); + var composeDoc = require_compose_doc(); + var resolveEnd = require_resolve_end(); + function getErrorPos(src) { + if (typeof src === "number") + return [src, src + 1]; + if (Array.isArray(src)) + return src.length === 2 ? src : [src[0], src[1]]; + const { offset, source } = src; + return [offset, offset + (typeof source === "string" ? source.length : 1)]; + } + function parsePrelude(prelude) { + let comment = ""; + let atComment = false; + let afterEmptyLine = false; + for (let i = 0; i < prelude.length; ++i) { + const source = prelude[i]; + switch (source[0]) { + case "#": + comment += (comment === "" ? "" : afterEmptyLine ? "\n\n" : "\n") + (source.substring(1) || " "); + atComment = true; + afterEmptyLine = false; + break; + case "%": + if (prelude[i + 1]?.[0] !== "#") + i += 1; + atComment = false; + break; + default: + if (!atComment) + afterEmptyLine = true; + atComment = false; + } + } + return { comment, afterEmptyLine }; + } + var Composer = class { + constructor(options = {}) { + this.doc = null; + this.atDirectives = false; + this.prelude = []; + this.errors = []; + this.warnings = []; + this.onError = (source, code, message, warning) => { + const pos = getErrorPos(source); + if (warning) + this.warnings.push(new errors.YAMLWarning(pos, code, message)); + else + this.errors.push(new errors.YAMLParseError(pos, code, message)); + }; + this.directives = new directives.Directives({ version: options.version || "1.2" }); + this.options = options; + } + decorate(doc, afterDoc) { + const { comment, afterEmptyLine } = parsePrelude(this.prelude); + if (comment) { + const dc = doc.contents; + if (afterDoc) { + doc.comment = doc.comment ? `${doc.comment} +${comment}` : comment; + } else if (afterEmptyLine || doc.directives.docStart || !dc) { + doc.commentBefore = comment; + } else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) { + let it = dc.items[0]; + if (identity.isPair(it)) + it = it.key; + const cb = it.commentBefore; + it.commentBefore = cb ? `${comment} +${cb}` : comment; + } else { + const cb = dc.commentBefore; + dc.commentBefore = cb ? `${comment} +${cb}` : comment; + } + } + if (afterDoc) { + for (let i = 0; i < this.errors.length; ++i) + doc.errors.push(this.errors[i]); + for (let i = 0; i < this.warnings.length; ++i) + doc.warnings.push(this.warnings[i]); + } else { + doc.errors = this.errors; + doc.warnings = this.warnings; + } + this.prelude = []; + this.errors = []; + this.warnings = []; + } + /** + * Current stream status information. + * + * Mostly useful at the end of input for an empty stream. + */ + streamInfo() { + return { + comment: parsePrelude(this.prelude).comment, + directives: this.directives, + errors: this.errors, + warnings: this.warnings + }; + } + /** + * Compose tokens into documents. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *compose(tokens, forceDoc = false, endOffset = -1) { + for (const token of tokens) + yield* this.next(token); + yield* this.end(forceDoc, endOffset); + } + /** Advance the composer by one CST token. */ + *next(token) { + if (node_process.env.LOG_STREAM) + console.dir(token, { depth: null }); + switch (token.type) { + case "directive": + this.directives.add(token.source, (offset, message, warning) => { + const pos = getErrorPos(token); + pos[0] += offset; + this.onError(pos, "BAD_DIRECTIVE", message, warning); + }); + this.prelude.push(token.source); + this.atDirectives = true; + break; + case "document": { + const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError); + if (this.atDirectives && !doc.directives.docStart) + this.onError(token, "MISSING_CHAR", "Missing directives-end/doc-start indicator line"); + this.decorate(doc, false); + if (this.doc) + yield this.doc; + this.doc = doc; + this.atDirectives = false; + break; + } + case "byte-order-mark": + case "space": + break; + case "comment": + case "newline": + this.prelude.push(token.source); + break; + case "error": { + const msg = token.source ? `${token.message}: ${JSON.stringify(token.source)}` : token.message; + const error = new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg); + if (this.atDirectives || !this.doc) + this.errors.push(error); + else + this.doc.errors.push(error); + break; + } + case "doc-end": { + if (!this.doc) { + const msg = "Unexpected doc-end without preceding document"; + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", msg)); + break; + } + this.doc.directives.docEnd = true; + const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError); + this.decorate(this.doc, true); + if (end.comment) { + const dc = this.doc.comment; + this.doc.comment = dc ? `${dc} +${end.comment}` : end.comment; + } + this.doc.range[2] = end.offset; + break; + } + default: + this.errors.push(new errors.YAMLParseError(getErrorPos(token), "UNEXPECTED_TOKEN", `Unsupported token ${token.type}`)); + } + } + /** + * Call at end of input to yield any remaining document. + * + * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document. + * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly. + */ + *end(forceDoc = false, endOffset = -1) { + if (this.doc) { + this.decorate(this.doc, true); + yield this.doc; + this.doc = null; + } else if (forceDoc) { + const opts = Object.assign({ _directives: this.directives }, this.options); + const doc = new Document2.Document(void 0, opts); + if (this.atDirectives) + this.onError(endOffset, "MISSING_CHAR", "Missing directives-end indicator line"); + doc.range = [0, endOffset, endOffset]; + this.decorate(doc, false); + yield doc; + } + } + }; + exports.Composer = Composer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js +var require_cst_scalar = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-scalar.js"(exports) { + "use strict"; + var resolveBlockScalar = require_resolve_block_scalar(); + var resolveFlowScalar = require_resolve_flow_scalar(); + var errors = require_errors(); + var stringifyString = require_stringifyString(); + function resolveAsScalar(token, strict = true, onError) { + if (token) { + const _onError = (pos, code, message) => { + const offset = typeof pos === "number" ? pos : Array.isArray(pos) ? pos[0] : pos.offset; + if (onError) + onError(offset, code, message); + else + throw new errors.YAMLParseError([offset, offset + 1], code, message); + }; + switch (token.type) { + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return resolveFlowScalar.resolveFlowScalar(token, strict, _onError); + case "block-scalar": + return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError); + } + } + return null; + } + function createScalarToken(value, context) { + const { implicitKey = false, indent, inFlow = false, offset = -1, type = "PLAIN" } = context; + const source = stringifyString.stringifyString({ type, value }, { + implicitKey, + indent: indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + const end = context.end ?? [ + { type: "newline", offset: -1, indent, source: "\n" } + ]; + switch (source[0]) { + case "|": + case ">": { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, end)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + return { type: "block-scalar", offset, indent, props, source: body }; + } + case '"': + return { type: "double-quoted-scalar", offset, indent, source, end }; + case "'": + return { type: "single-quoted-scalar", offset, indent, source, end }; + default: + return { type: "scalar", offset, indent, source, end }; + } + } + function setScalarValue(token, value, context = {}) { + let { afterKey = false, implicitKey = false, inFlow = false, type } = context; + let indent = "indent" in token ? token.indent : null; + if (afterKey && typeof indent === "number") + indent += 2; + if (!type) + switch (token.type) { + case "single-quoted-scalar": + type = "QUOTE_SINGLE"; + break; + case "double-quoted-scalar": + type = "QUOTE_DOUBLE"; + break; + case "block-scalar": { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + type = header.source[0] === ">" ? "BLOCK_FOLDED" : "BLOCK_LITERAL"; + break; + } + default: + type = "PLAIN"; + } + const source = stringifyString.stringifyString({ type, value }, { + implicitKey: implicitKey || indent === null, + indent: indent !== null && indent > 0 ? " ".repeat(indent) : "", + inFlow, + options: { blockQuote: true, lineWidth: -1 } + }); + switch (source[0]) { + case "|": + case ">": + setBlockScalarValue(token, source); + break; + case '"': + setFlowScalarValue(token, source, "double-quoted-scalar"); + break; + case "'": + setFlowScalarValue(token, source, "single-quoted-scalar"); + break; + default: + setFlowScalarValue(token, source, "scalar"); + } + } + function setBlockScalarValue(token, source) { + const he = source.indexOf("\n"); + const head = source.substring(0, he); + const body = source.substring(he + 1) + "\n"; + if (token.type === "block-scalar") { + const header = token.props[0]; + if (header.type !== "block-scalar-header") + throw new Error("Invalid block scalar header"); + header.source = head; + token.source = body; + } else { + const { offset } = token; + const indent = "indent" in token ? token.indent : -1; + const props = [ + { type: "block-scalar-header", offset, indent, source: head } + ]; + if (!addEndtoBlockProps(props, "end" in token ? token.end : void 0)) + props.push({ type: "newline", offset: -1, indent, source: "\n" }); + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type: "block-scalar", indent, props, source: body }); + } + } + function addEndtoBlockProps(props, end) { + if (end) + for (const st of end) + switch (st.type) { + case "space": + case "comment": + props.push(st); + break; + case "newline": + props.push(st); + return true; + } + return false; + } + function setFlowScalarValue(token, source, type) { + switch (token.type) { + case "scalar": + case "double-quoted-scalar": + case "single-quoted-scalar": + token.type = type; + token.source = source; + break; + case "block-scalar": { + const end = token.props.slice(1); + let oa = source.length; + if (token.props[0].type === "block-scalar-header") + oa -= token.props[0].source.length; + for (const tok of end) + tok.offset += oa; + delete token.props; + Object.assign(token, { type, source, end }); + break; + } + case "block-map": + case "block-seq": { + const offset = token.offset + source.length; + const nl = { type: "newline", offset, indent: token.indent, source: "\n" }; + delete token.items; + Object.assign(token, { type, source, end: [nl] }); + break; + } + default: { + const indent = "indent" in token ? token.indent : -1; + const end = "end" in token && Array.isArray(token.end) ? token.end.filter((st) => st.type === "space" || st.type === "comment" || st.type === "newline") : []; + for (const key of Object.keys(token)) + if (key !== "type" && key !== "offset") + delete token[key]; + Object.assign(token, { type, indent, source, end }); + } + } + } + exports.createScalarToken = createScalarToken; + exports.resolveAsScalar = resolveAsScalar; + exports.setScalarValue = setScalarValue; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js +var require_cst_stringify = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-stringify.js"(exports) { + "use strict"; + var stringify3 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst); + function stringifyToken(token) { + switch (token.type) { + case "block-scalar": { + let res = ""; + for (const tok of token.props) + res += stringifyToken(tok); + return res + token.source; + } + case "block-map": + case "block-seq": { + let res = ""; + for (const item of token.items) + res += stringifyItem(item); + return res; + } + case "flow-collection": { + let res = token.start.source; + for (const item of token.items) + res += stringifyItem(item); + for (const st of token.end) + res += st.source; + return res; + } + case "document": { + let res = stringifyItem(token); + if (token.end) + for (const st of token.end) + res += st.source; + return res; + } + default: { + let res = token.source; + if ("end" in token && token.end) + for (const st of token.end) + res += st.source; + return res; + } + } + } + function stringifyItem({ start, key, sep, value }) { + let res = ""; + for (const st of start) + res += st.source; + if (key) + res += stringifyToken(key); + if (sep) + for (const st of sep) + res += st.source; + if (value) + res += stringifyToken(value); + return res; + } + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js +var require_cst_visit = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst-visit.js"(exports) { + "use strict"; + var BREAK = /* @__PURE__ */ Symbol("break visit"); + var SKIP = /* @__PURE__ */ Symbol("skip children"); + var REMOVE = /* @__PURE__ */ Symbol("remove item"); + function visit(cst, visitor) { + if ("type" in cst && cst.type === "document") + cst = { start: cst.start, value: cst.value }; + _visit(Object.freeze([]), cst, visitor); + } + visit.BREAK = BREAK; + visit.SKIP = SKIP; + visit.REMOVE = REMOVE; + visit.itemAtPath = (cst, path14) => { + let item = cst; + for (const [field2, index] of path14) { + const tok = item?.[field2]; + if (tok && "items" in tok) { + item = tok.items[index]; + } else + return void 0; + } + return item; + }; + visit.parentCollection = (cst, path14) => { + const parent = visit.itemAtPath(cst, path14.slice(0, -1)); + const field2 = path14[path14.length - 1][0]; + const coll = parent?.[field2]; + if (coll && "items" in coll) + return coll; + throw new Error("Parent collection not found"); + }; + function _visit(path14, item, visitor) { + let ctrl = visitor(item, path14); + if (typeof ctrl === "symbol") + return ctrl; + for (const field2 of ["key", "value"]) { + const token = item[field2]; + if (token && "items" in token) { + for (let i = 0; i < token.items.length; ++i) { + const ci = _visit(Object.freeze(path14.concat([[field2, i]])), token.items[i], visitor); + if (typeof ci === "number") + i = ci - 1; + else if (ci === BREAK) + return BREAK; + else if (ci === REMOVE) { + token.items.splice(i, 1); + i -= 1; + } + } + if (typeof ctrl === "function" && field2 === "key") + ctrl = ctrl(item, path14); + } + } + return typeof ctrl === "function" ? ctrl(item, path14) : ctrl; + } + exports.visit = visit; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js +var require_cst = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/cst.js"(exports) { + "use strict"; + var cstScalar = require_cst_scalar(); + var cstStringify = require_cst_stringify(); + var cstVisit = require_cst_visit(); + var BOM = "\uFEFF"; + var DOCUMENT = ""; + var FLOW_END = ""; + var SCALAR = ""; + var isCollection = (token) => !!token && "items" in token; + var isScalar = (token) => !!token && (token.type === "scalar" || token.type === "single-quoted-scalar" || token.type === "double-quoted-scalar" || token.type === "block-scalar"); + function prettyToken(token) { + switch (token) { + case BOM: + return ""; + case DOCUMENT: + return ""; + case FLOW_END: + return ""; + case SCALAR: + return ""; + default: + return JSON.stringify(token); + } + } + function tokenType(source) { + switch (source) { + case BOM: + return "byte-order-mark"; + case DOCUMENT: + return "doc-mode"; + case FLOW_END: + return "flow-error-end"; + case SCALAR: + return "scalar"; + case "---": + return "doc-start"; + case "...": + return "doc-end"; + case "": + case "\n": + case "\r\n": + return "newline"; + case "-": + return "seq-item-ind"; + case "?": + return "explicit-key-ind"; + case ":": + return "map-value-ind"; + case "{": + return "flow-map-start"; + case "}": + return "flow-map-end"; + case "[": + return "flow-seq-start"; + case "]": + return "flow-seq-end"; + case ",": + return "comma"; + } + switch (source[0]) { + case " ": + case " ": + return "space"; + case "#": + return "comment"; + case "%": + return "directive-line"; + case "*": + return "alias"; + case "&": + return "anchor"; + case "!": + return "tag"; + case "'": + return "single-quoted-scalar"; + case '"': + return "double-quoted-scalar"; + case "|": + case ">": + return "block-scalar-header"; + } + return null; + } + exports.createScalarToken = cstScalar.createScalarToken; + exports.resolveAsScalar = cstScalar.resolveAsScalar; + exports.setScalarValue = cstScalar.setScalarValue; + exports.stringify = cstStringify.stringify; + exports.visit = cstVisit.visit; + exports.BOM = BOM; + exports.DOCUMENT = DOCUMENT; + exports.FLOW_END = FLOW_END; + exports.SCALAR = SCALAR; + exports.isCollection = isCollection; + exports.isScalar = isScalar; + exports.prettyToken = prettyToken; + exports.tokenType = tokenType; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js +var require_lexer = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/lexer.js"(exports) { + "use strict"; + var cst = require_cst(); + function isEmpty(ch) { + switch (ch) { + case void 0: + case " ": + case "\n": + case "\r": + case " ": + return true; + default: + return false; + } + } + var hexDigits = new Set("0123456789ABCDEFabcdef"); + var tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"); + var flowIndicatorChars = new Set(",[]{}"); + var invalidAnchorChars = new Set(" ,[]{}\n\r "); + var isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch); + var Lexer = class { + constructor() { + this.atEnd = false; + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + this.buffer = ""; + this.flowKey = false; + this.flowLevel = 0; + this.indentNext = 0; + this.indentValue = 0; + this.lineEndPos = null; + this.next = null; + this.pos = 0; + } + /** + * Generate YAML tokens from the `source` string. If `incomplete`, + * a part of the last line may be left as a buffer for the next call. + * + * @returns A generator of lexical tokens + */ + *lex(source, incomplete = false) { + if (source) { + if (typeof source !== "string") + throw TypeError("source is not a string"); + this.buffer = this.buffer ? this.buffer + source : source; + this.lineEndPos = null; + } + this.atEnd = !incomplete; + let next = this.next ?? "stream"; + while (next && (incomplete || this.hasChars(1))) + next = yield* this.parseNext(next); + } + atLineEnd() { + let i = this.pos; + let ch = this.buffer[i]; + while (ch === " " || ch === " ") + ch = this.buffer[++i]; + if (!ch || ch === "#" || ch === "\n") + return true; + if (ch === "\r") + return this.buffer[i + 1] === "\n"; + return false; + } + charAt(n) { + return this.buffer[this.pos + n]; + } + continueScalar(offset) { + let ch = this.buffer[offset]; + if (this.indentNext > 0) { + let indent = 0; + while (ch === " ") + ch = this.buffer[++indent + offset]; + if (ch === "\r") { + const next = this.buffer[indent + offset + 1]; + if (next === "\n" || !next && !this.atEnd) + return offset + indent + 1; + } + return ch === "\n" || indent >= this.indentNext || !ch && !this.atEnd ? offset + indent : -1; + } + if (ch === "-" || ch === ".") { + const dt = this.buffer.substr(offset, 3); + if ((dt === "---" || dt === "...") && isEmpty(this.buffer[offset + 3])) + return -1; + } + return offset; + } + getLine() { + let end = this.lineEndPos; + if (typeof end !== "number" || end !== -1 && end < this.pos) { + end = this.buffer.indexOf("\n", this.pos); + this.lineEndPos = end; + } + if (end === -1) + return this.atEnd ? this.buffer.substring(this.pos) : null; + if (this.buffer[end - 1] === "\r") + end -= 1; + return this.buffer.substring(this.pos, end); + } + hasChars(n) { + return this.pos + n <= this.buffer.length; + } + setNext(state) { + this.buffer = this.buffer.substring(this.pos); + this.pos = 0; + this.lineEndPos = null; + this.next = state; + return null; + } + peek(n) { + return this.buffer.substr(this.pos, n); + } + *parseNext(next) { + switch (next) { + case "stream": + return yield* this.parseStream(); + case "line-start": + return yield* this.parseLineStart(); + case "block-start": + return yield* this.parseBlockStart(); + case "doc": + return yield* this.parseDocument(); + case "flow": + return yield* this.parseFlowCollection(); + case "quoted-scalar": + return yield* this.parseQuotedScalar(); + case "block-scalar": + return yield* this.parseBlockScalar(); + case "plain-scalar": + return yield* this.parsePlainScalar(); + } + } + *parseStream() { + let line = this.getLine(); + if (line === null) + return this.setNext("stream"); + if (line[0] === cst.BOM) { + yield* this.pushCount(1); + line = line.substring(1); + } + if (line[0] === "%") { + let dirEnd = line.length; + let cs = line.indexOf("#"); + while (cs !== -1) { + const ch = line[cs - 1]; + if (ch === " " || ch === " ") { + dirEnd = cs - 1; + break; + } else { + cs = line.indexOf("#", cs + 1); + } + } + while (true) { + const ch = line[dirEnd - 1]; + if (ch === " " || ch === " ") + dirEnd -= 1; + else + break; + } + const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true)); + yield* this.pushCount(line.length - n); + this.pushNewline(); + return "stream"; + } + if (this.atLineEnd()) { + const sp = yield* this.pushSpaces(true); + yield* this.pushCount(line.length - sp); + yield* this.pushNewline(); + return "stream"; + } + yield cst.DOCUMENT; + return yield* this.parseLineStart(); + } + *parseLineStart() { + const ch = this.charAt(0); + if (!ch && !this.atEnd) + return this.setNext("line-start"); + if (ch === "-" || ch === ".") { + if (!this.atEnd && !this.hasChars(4)) + return this.setNext("line-start"); + const s = this.peek(3); + if ((s === "---" || s === "...") && isEmpty(this.charAt(3))) { + yield* this.pushCount(3); + this.indentValue = 0; + this.indentNext = 0; + return s === "---" ? "doc" : "stream"; + } + } + this.indentValue = yield* this.pushSpaces(false); + if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1))) + this.indentNext = this.indentValue; + return yield* this.parseBlockStart(); + } + *parseBlockStart() { + const [ch0, ch1] = this.peek(2); + if (!ch1 && !this.atEnd) + return this.setNext("block-start"); + if ((ch0 === "-" || ch0 === "?" || ch0 === ":") && isEmpty(ch1)) { + const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true)); + this.indentNext = this.indentValue + 1; + this.indentValue += n; + return "block-start"; + } + return "doc"; + } + *parseDocument() { + yield* this.pushSpaces(true); + const line = this.getLine(); + if (line === null) + return this.setNext("doc"); + let n = yield* this.pushIndicators(); + switch (line[n]) { + case "#": + yield* this.pushCount(line.length - n); + // fallthrough + case void 0: + yield* this.pushNewline(); + return yield* this.parseLineStart(); + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel = 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + return "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "doc"; + case '"': + case "'": + return yield* this.parseQuotedScalar(); + case "|": + case ">": + n += yield* this.parseBlockScalarHeader(); + n += yield* this.pushSpaces(true); + yield* this.pushCount(line.length - n); + yield* this.pushNewline(); + return yield* this.parseBlockScalar(); + default: + return yield* this.parsePlainScalar(); + } + } + *parseFlowCollection() { + let nl, sp; + let indent = -1; + do { + nl = yield* this.pushNewline(); + if (nl > 0) { + sp = yield* this.pushSpaces(false); + this.indentValue = indent = sp; + } else { + sp = 0; + } + sp += yield* this.pushSpaces(true); + } while (nl + sp > 0); + const line = this.getLine(); + if (line === null) + return this.setNext("flow"); + if (indent !== -1 && indent < this.indentNext && line[0] !== "#" || indent === 0 && (line.startsWith("---") || line.startsWith("...")) && isEmpty(line[3])) { + const atFlowEndMarker = indent === this.indentNext - 1 && this.flowLevel === 1 && (line[0] === "]" || line[0] === "}"); + if (!atFlowEndMarker) { + this.flowLevel = 0; + yield cst.FLOW_END; + return yield* this.parseLineStart(); + } + } + let n = 0; + while (line[n] === ",") { + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + this.flowKey = false; + } + n += yield* this.pushIndicators(); + switch (line[n]) { + case void 0: + return "flow"; + case "#": + yield* this.pushCount(line.length - n); + return "flow"; + case "{": + case "[": + yield* this.pushCount(1); + this.flowKey = false; + this.flowLevel += 1; + return "flow"; + case "}": + case "]": + yield* this.pushCount(1); + this.flowKey = true; + this.flowLevel -= 1; + return this.flowLevel ? "flow" : "doc"; + case "*": + yield* this.pushUntil(isNotAnchorChar); + return "flow"; + case '"': + case "'": + this.flowKey = true; + return yield* this.parseQuotedScalar(); + case ":": { + const next = this.charAt(1); + if (this.flowKey || isEmpty(next) || next === ",") { + this.flowKey = false; + yield* this.pushCount(1); + yield* this.pushSpaces(true); + return "flow"; + } + } + // fallthrough + default: + this.flowKey = false; + return yield* this.parsePlainScalar(); + } + } + *parseQuotedScalar() { + const quote = this.charAt(0); + let end = this.buffer.indexOf(quote, this.pos + 1); + if (quote === "'") { + while (end !== -1 && this.buffer[end + 1] === "'") + end = this.buffer.indexOf("'", end + 2); + } else { + while (end !== -1) { + let n = 0; + while (this.buffer[end - 1 - n] === "\\") + n += 1; + if (n % 2 === 0) + break; + end = this.buffer.indexOf('"', end + 1); + } + } + const qb = this.buffer.substring(0, end); + let nl = qb.indexOf("\n", this.pos); + if (nl !== -1) { + while (nl !== -1) { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = qb.indexOf("\n", cs); + } + if (nl !== -1) { + end = nl - (qb[nl - 1] === "\r" ? 2 : 1); + } + } + if (end === -1) { + if (!this.atEnd) + return this.setNext("quoted-scalar"); + end = this.buffer.length; + } + yield* this.pushToIndex(end + 1, false); + return this.flowLevel ? "flow" : "doc"; + } + *parseBlockScalarHeader() { + this.blockScalarIndent = -1; + this.blockScalarKeep = false; + let i = this.pos; + while (true) { + const ch = this.buffer[++i]; + if (ch === "+") + this.blockScalarKeep = true; + else if (ch > "0" && ch <= "9") + this.blockScalarIndent = Number(ch) - 1; + else if (ch !== "-") + break; + } + return yield* this.pushUntil((ch) => isEmpty(ch) || ch === "#"); + } + *parseBlockScalar() { + let nl = this.pos - 1; + let indent = 0; + let ch; + loop: for (let i2 = this.pos; ch = this.buffer[i2]; ++i2) { + switch (ch) { + case " ": + indent += 1; + break; + case "\n": + nl = i2; + indent = 0; + break; + case "\r": { + const next = this.buffer[i2 + 1]; + if (!next && !this.atEnd) + return this.setNext("block-scalar"); + if (next === "\n") + break; + } + // fallthrough + default: + break loop; + } + } + if (!ch && !this.atEnd) + return this.setNext("block-scalar"); + if (indent >= this.indentNext) { + if (this.blockScalarIndent === -1) + this.indentNext = indent; + else { + this.indentNext = this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext); + } + do { + const cs = this.continueScalar(nl + 1); + if (cs === -1) + break; + nl = this.buffer.indexOf("\n", cs); + } while (nl !== -1); + if (nl === -1) { + if (!this.atEnd) + return this.setNext("block-scalar"); + nl = this.buffer.length; + } + } + let i = nl + 1; + ch = this.buffer[i]; + while (ch === " ") + ch = this.buffer[++i]; + if (ch === " ") { + while (ch === " " || ch === " " || ch === "\r" || ch === "\n") + ch = this.buffer[++i]; + nl = i - 1; + } else if (!this.blockScalarKeep) { + do { + let i2 = nl - 1; + let ch2 = this.buffer[i2]; + if (ch2 === "\r") + ch2 = this.buffer[--i2]; + const lastChar = i2; + while (ch2 === " ") + ch2 = this.buffer[--i2]; + if (ch2 === "\n" && i2 >= this.pos && i2 + 1 + indent > lastChar) + nl = i2; + else + break; + } while (true); + } + yield cst.SCALAR; + yield* this.pushToIndex(nl + 1, true); + return yield* this.parseLineStart(); + } + *parsePlainScalar() { + const inFlow = this.flowLevel > 0; + let end = this.pos - 1; + let i = this.pos - 1; + let ch; + while (ch = this.buffer[++i]) { + if (ch === ":") { + const next = this.buffer[i + 1]; + if (isEmpty(next) || inFlow && flowIndicatorChars.has(next)) + break; + end = i; + } else if (isEmpty(ch)) { + let next = this.buffer[i + 1]; + if (ch === "\r") { + if (next === "\n") { + i += 1; + ch = "\n"; + next = this.buffer[i + 1]; + } else + end = i; + } + if (next === "#" || inFlow && flowIndicatorChars.has(next)) + break; + if (ch === "\n") { + const cs = this.continueScalar(i + 1); + if (cs === -1) + break; + i = Math.max(i, cs - 2); + } + } else { + if (inFlow && flowIndicatorChars.has(ch)) + break; + end = i; + } + } + if (!ch && !this.atEnd) + return this.setNext("plain-scalar"); + yield cst.SCALAR; + yield* this.pushToIndex(end + 1, true); + return inFlow ? "flow" : "doc"; + } + *pushCount(n) { + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos += n; + return n; + } + return 0; + } + *pushToIndex(i, allowEmpty) { + const s = this.buffer.slice(this.pos, i); + if (s) { + yield s; + this.pos += s.length; + return s.length; + } else if (allowEmpty) + yield ""; + return 0; + } + *pushIndicators() { + let n = 0; + loop: while (true) { + switch (this.charAt(0)) { + case "!": + n += yield* this.pushTag(); + n += yield* this.pushSpaces(true); + continue loop; + case "&": + n += yield* this.pushUntil(isNotAnchorChar); + n += yield* this.pushSpaces(true); + continue loop; + case "-": + // this is an error + case "?": + // this is an error outside flow collections + case ":": { + const inFlow = this.flowLevel > 0; + const ch1 = this.charAt(1); + if (isEmpty(ch1) || inFlow && flowIndicatorChars.has(ch1)) { + if (!inFlow) + this.indentNext = this.indentValue + 1; + else if (this.flowKey) + this.flowKey = false; + n += yield* this.pushCount(1); + n += yield* this.pushSpaces(true); + continue loop; + } + } + } + break loop; + } + return n; + } + *pushTag() { + if (this.charAt(1) === "<") { + let i = this.pos + 2; + let ch = this.buffer[i]; + while (!isEmpty(ch) && ch !== ">") + ch = this.buffer[++i]; + return yield* this.pushToIndex(ch === ">" ? i + 1 : i, false); + } else { + let i = this.pos + 1; + let ch = this.buffer[i]; + while (ch) { + if (tagChars.has(ch)) + ch = this.buffer[++i]; + else if (ch === "%" && hexDigits.has(this.buffer[i + 1]) && hexDigits.has(this.buffer[i + 2])) { + ch = this.buffer[i += 3]; + } else + break; + } + return yield* this.pushToIndex(i, false); + } + } + *pushNewline() { + const ch = this.buffer[this.pos]; + if (ch === "\n") + return yield* this.pushCount(1); + else if (ch === "\r" && this.charAt(1) === "\n") + return yield* this.pushCount(2); + else + return 0; + } + *pushSpaces(allowTabs) { + let i = this.pos - 1; + let ch; + do { + ch = this.buffer[++i]; + } while (ch === " " || allowTabs && ch === " "); + const n = i - this.pos; + if (n > 0) { + yield this.buffer.substr(this.pos, n); + this.pos = i; + } + return n; + } + *pushUntil(test) { + let i = this.pos; + let ch = this.buffer[i]; + while (!test(ch)) + ch = this.buffer[++i]; + return yield* this.pushToIndex(i, false); + } + }; + exports.Lexer = Lexer; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js +var require_line_counter = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/line-counter.js"(exports) { + "use strict"; + var LineCounter = class { + constructor() { + this.lineStarts = []; + this.addNewLine = (offset) => this.lineStarts.push(offset); + this.linePos = (offset) => { + let low = 0; + let high = this.lineStarts.length; + while (low < high) { + const mid = low + high >> 1; + if (this.lineStarts[mid] < offset) + low = mid + 1; + else + high = mid; + } + if (this.lineStarts[low] === offset) + return { line: low + 1, col: 1 }; + if (low === 0) + return { line: 0, col: offset }; + const start = this.lineStarts[low - 1]; + return { line: low, col: offset - start + 1 }; + }; + } + }; + exports.LineCounter = LineCounter; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js +var require_parser = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/parse/parser.js"(exports) { + "use strict"; + var node_process = __require("process"); + var cst = require_cst(); + var lexer = require_lexer(); + function includesToken(list, type) { + for (let i = 0; i < list.length; ++i) + if (list[i].type === type) + return true; + return false; + } + function findNonEmptyIndex(list) { + for (let i = 0; i < list.length; ++i) { + switch (list[i].type) { + case "space": + case "comment": + case "newline": + break; + default: + return i; + } + } + return -1; + } + function isFlowToken(token) { + switch (token?.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + case "flow-collection": + return true; + default: + return false; + } + } + function getPrevProps(parent) { + switch (parent.type) { + case "document": + return parent.start; + case "block-map": { + const it = parent.items[parent.items.length - 1]; + return it.sep ?? it.start; + } + case "block-seq": + return parent.items[parent.items.length - 1].start; + /* istanbul ignore next should not happen */ + default: + return []; + } + } + function getFirstKeyStartProps(prev) { + if (prev.length === 0) + return []; + let i = prev.length; + loop: while (--i >= 0) { + switch (prev[i].type) { + case "doc-start": + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + case "newline": + break loop; + } + } + while (prev[++i]?.type === "space") { + } + return prev.splice(i, prev.length); + } + function arrayPushArray(target, source) { + if (source.length < 1e5) + Array.prototype.push.apply(target, source); + else + for (let i = 0; i < source.length; ++i) + target.push(source[i]); + } + function fixFlowSeqItems(fc) { + if (fc.start.type === "flow-seq-start") { + for (const it of fc.items) { + if (it.sep && !it.value && !includesToken(it.start, "explicit-key-ind") && !includesToken(it.sep, "map-value-ind")) { + if (it.key) + it.value = it.key; + delete it.key; + if (isFlowToken(it.value)) { + if (it.value.end) + arrayPushArray(it.value.end, it.sep); + else + it.value.end = it.sep; + } else + arrayPushArray(it.start, it.sep); + delete it.sep; + } + } + } + } + var Parser = class { + /** + * @param onNewLine - If defined, called separately with the start position of + * each new line (in `parse()`, including the start of input). + */ + constructor(onNewLine) { + this.atNewLine = true; + this.atScalar = false; + this.indent = 0; + this.offset = 0; + this.onKeyLine = false; + this.stack = []; + this.source = ""; + this.type = ""; + this.lexer = new lexer.Lexer(); + this.onNewLine = onNewLine; + } + /** + * Parse `source` as a YAML stream. + * If `incomplete`, a part of the last line may be left as a buffer for the next call. + * + * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens. + * + * @returns A generator of tokens representing each directive, document, and other structure. + */ + *parse(source, incomplete = false) { + if (this.onNewLine && this.offset === 0) + this.onNewLine(0); + for (const lexeme of this.lexer.lex(source, incomplete)) + yield* this.next(lexeme); + if (!incomplete) + yield* this.end(); + } + /** + * Advance the parser by the `source` of one lexical token. + */ + *next(source) { + this.source = source; + if (node_process.env.LOG_TOKENS) + console.log("|", cst.prettyToken(source)); + if (this.atScalar) { + this.atScalar = false; + yield* this.step(); + this.offset += source.length; + return; + } + const type = cst.tokenType(source); + if (!type) { + const message = `Not a YAML token: ${source}`; + yield* this.pop({ type: "error", offset: this.offset, message, source }); + this.offset += source.length; + } else if (type === "scalar") { + this.atNewLine = false; + this.atScalar = true; + this.type = "scalar"; + } else { + this.type = type; + yield* this.step(); + switch (type) { + case "newline": + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) + this.onNewLine(this.offset + source.length); + break; + case "space": + if (this.atNewLine && source[0] === " ") + this.indent += source.length; + break; + case "explicit-key-ind": + case "map-value-ind": + case "seq-item-ind": + if (this.atNewLine) + this.indent += source.length; + break; + case "doc-mode": + case "flow-error-end": + return; + default: + this.atNewLine = false; + } + this.offset += source.length; + } + } + /** Call at end of input to push out any remaining constructions */ + *end() { + while (this.stack.length > 0) + yield* this.pop(); + } + get sourceToken() { + const st = { + type: this.type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + return st; + } + *step() { + const top = this.peek(1); + if (this.type === "doc-end" && top?.type !== "doc-end") { + while (this.stack.length > 0) + yield* this.pop(); + this.stack.push({ + type: "doc-end", + offset: this.offset, + source: this.source + }); + return; + } + if (!top) + return yield* this.stream(); + switch (top.type) { + case "document": + return yield* this.document(top); + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return yield* this.scalar(top); + case "block-scalar": + return yield* this.blockScalar(top); + case "block-map": + return yield* this.blockMap(top); + case "block-seq": + return yield* this.blockSequence(top); + case "flow-collection": + return yield* this.flowCollection(top); + case "doc-end": + return yield* this.documentEnd(top); + } + yield* this.pop(); + } + peek(n) { + return this.stack[this.stack.length - n]; + } + *pop(error) { + const token = error ?? this.stack.pop(); + if (!token) { + const message = "Tried to pop an empty stack"; + yield { type: "error", offset: this.offset, source: "", message }; + } else if (this.stack.length === 0) { + yield token; + } else { + const top = this.peek(1); + if (token.type === "block-scalar") { + token.indent = "indent" in top ? top.indent : 0; + } else if (token.type === "flow-collection" && top.type === "document") { + token.indent = 0; + } + if (token.type === "flow-collection") + fixFlowSeqItems(token); + switch (top.type) { + case "document": + top.value = token; + break; + case "block-scalar": + top.props.push(token); + break; + case "block-map": { + const it = top.items[top.items.length - 1]; + if (it.value) { + top.items.push({ start: [], key: token, sep: [] }); + this.onKeyLine = true; + return; + } else if (it.sep) { + it.value = token; + } else { + Object.assign(it, { key: token, sep: [] }); + this.onKeyLine = !it.explicitKey; + return; + } + break; + } + case "block-seq": { + const it = top.items[top.items.length - 1]; + if (it.value) + top.items.push({ start: [], value: token }); + else + it.value = token; + break; + } + case "flow-collection": { + const it = top.items[top.items.length - 1]; + if (!it || it.value) + top.items.push({ start: [], key: token, sep: [] }); + else if (it.sep) + it.value = token; + else + Object.assign(it, { key: token, sep: [] }); + return; + } + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.pop(token); + } + if ((top.type === "document" || top.type === "block-map" || top.type === "block-seq") && (token.type === "block-map" || token.type === "block-seq")) { + const last = token.items[token.items.length - 1]; + if (last && !last.sep && !last.value && last.start.length > 0 && findNonEmptyIndex(last.start) === -1 && (token.indent === 0 || last.start.every((st) => st.type !== "comment" || st.indent < token.indent))) { + if (top.type === "document") + top.end = last.start; + else + top.items.push({ start: last.start }); + token.items.splice(-1, 1); + } + } + } + } + *stream() { + switch (this.type) { + case "directive-line": + yield { type: "directive", offset: this.offset, source: this.source }; + return; + case "byte-order-mark": + case "space": + case "comment": + case "newline": + yield this.sourceToken; + return; + case "doc-mode": + case "doc-start": { + const doc = { + type: "document", + offset: this.offset, + start: [] + }; + if (this.type === "doc-start") + doc.start.push(this.sourceToken); + this.stack.push(doc); + return; + } + } + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML stream`, + source: this.source + }; + } + *document(doc) { + if (doc.value) + return yield* this.lineEnd(doc); + switch (this.type) { + case "doc-start": { + if (findNonEmptyIndex(doc.start) !== -1) { + yield* this.pop(); + yield* this.step(); + } else + doc.start.push(this.sourceToken); + return; + } + case "anchor": + case "tag": + case "space": + case "comment": + case "newline": + doc.start.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(doc); + if (bv) + this.stack.push(bv); + else { + yield { + type: "error", + offset: this.offset, + message: `Unexpected ${this.type} token in YAML document`, + source: this.source + }; + } + } + *scalar(scalar) { + if (this.type === "map-value-ind") { + const prev = getPrevProps(this.peek(2)); + const start = getFirstKeyStartProps(prev); + let sep; + if (scalar.end) { + sep = scalar.end; + sep.push(this.sourceToken); + delete scalar.end; + } else + sep = [this.sourceToken]; + const map = { + type: "block-map", + offset: scalar.offset, + indent: scalar.indent, + items: [{ start, key: scalar, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else + yield* this.lineEnd(scalar); + } + *blockScalar(scalar) { + switch (this.type) { + case "space": + case "comment": + case "newline": + scalar.props.push(this.sourceToken); + return; + case "scalar": + scalar.source = this.source; + this.atNewLine = true; + this.indent = 0; + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + yield* this.pop(); + break; + /* istanbul ignore next should not happen */ + default: + yield* this.pop(); + yield* this.step(); + } + } + *blockMap(map) { + const it = map.items[map.items.length - 1]; + switch (this.type) { + case "newline": + this.onKeyLine = false; + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "space": + case "comment": + if (it.value) { + map.items.push({ start: [this.sourceToken] }); + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + if (this.atIndentedComment(it.start, map.indent)) { + const prev = map.items[map.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + map.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + } + if (this.indent >= map.indent) { + const atMapIndent = !this.onKeyLine && this.indent === map.indent; + const atNextItem = atMapIndent && (it.sep || it.explicitKey) && this.type !== "seq-item-ind"; + let start = []; + if (atNextItem && it.sep && !it.value) { + const nl = []; + for (let i = 0; i < it.sep.length; ++i) { + const st = it.sep[i]; + switch (st.type) { + case "newline": + nl.push(i); + break; + case "space": + break; + case "comment": + if (st.indent > map.indent) + nl.length = 0; + break; + default: + nl.length = 0; + } + } + if (nl.length >= 2) + start = it.sep.splice(nl[1]); + } + switch (this.type) { + case "anchor": + case "tag": + if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start }); + this.onKeyLine = true; + } else if (it.sep) { + it.sep.push(this.sourceToken); + } else { + it.start.push(this.sourceToken); + } + return; + case "explicit-key-ind": + if (!it.sep && !it.explicitKey) { + it.start.push(this.sourceToken); + it.explicitKey = true; + } else if (atNextItem || it.value) { + start.push(this.sourceToken); + map.items.push({ start, explicitKey: true }); + } else { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken], explicitKey: true }] + }); + } + this.onKeyLine = true; + return; + case "map-value-ind": + if (it.explicitKey) { + if (!it.sep) { + if (includesToken(it.start, "newline")) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else { + const start2 = getFirstKeyStartProps(it.start); + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key: null, sep: [this.sourceToken] }] + }); + } + } else if (it.value) { + map.items.push({ start: [], key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }); + } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) { + const start2 = getFirstKeyStartProps(it.start); + const key = it.key; + const sep = it.sep; + sep.push(this.sourceToken); + delete it.key; + delete it.sep; + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: start2, key, sep }] + }); + } else if (start.length > 0) { + it.sep = it.sep.concat(start, this.sourceToken); + } else { + it.sep.push(this.sourceToken); + } + } else { + if (!it.sep) { + Object.assign(it, { key: null, sep: [this.sourceToken] }); + } else if (it.value || atNextItem) { + map.items.push({ start, key: null, sep: [this.sourceToken] }); + } else if (includesToken(it.sep, "map-value-ind")) { + this.stack.push({ + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start: [], key: null, sep: [this.sourceToken] }] + }); + } else { + it.sep.push(this.sourceToken); + } + } + this.onKeyLine = true; + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs14 = this.flowScalar(this.type); + if (atNextItem || it.value) { + map.items.push({ start, key: fs14, sep: [] }); + this.onKeyLine = true; + } else if (it.sep) { + this.stack.push(fs14); + } else { + Object.assign(it, { key: fs14, sep: [] }); + this.onKeyLine = true; + } + return; + } + default: { + const bv = this.startBlockValue(map); + if (bv) { + if (bv.type === "block-seq") { + if (!it.explicitKey && it.sep && !includesToken(it.sep, "newline")) { + yield* this.pop({ + type: "error", + offset: this.offset, + message: "Unexpected block-seq-ind on same line with key", + source: this.source + }); + return; + } + } else if (atMapIndent) { + map.items.push({ start }); + } + this.stack.push(bv); + return; + } + } + } + } + yield* this.pop(); + yield* this.step(); + } + *blockSequence(seq) { + const it = seq.items[seq.items.length - 1]; + switch (this.type) { + case "newline": + if (it.value) { + const end = "end" in it.value ? it.value.end : void 0; + const last = Array.isArray(end) ? end[end.length - 1] : void 0; + if (last?.type === "comment") + end?.push(this.sourceToken); + else + seq.items.push({ start: [this.sourceToken] }); + } else + it.start.push(this.sourceToken); + return; + case "space": + case "comment": + if (it.value) + seq.items.push({ start: [this.sourceToken] }); + else { + if (this.atIndentedComment(it.start, seq.indent)) { + const prev = seq.items[seq.items.length - 2]; + const end = prev?.value?.end; + if (Array.isArray(end)) { + arrayPushArray(end, it.start); + end.push(this.sourceToken); + seq.items.pop(); + return; + } + } + it.start.push(this.sourceToken); + } + return; + case "anchor": + case "tag": + if (it.value || this.indent <= seq.indent) + break; + it.start.push(this.sourceToken); + return; + case "seq-item-ind": + if (this.indent !== seq.indent) + break; + if (it.value || includesToken(it.start, "seq-item-ind")) + seq.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + } + if (this.indent > seq.indent) { + const bv = this.startBlockValue(seq); + if (bv) { + this.stack.push(bv); + return; + } + } + yield* this.pop(); + yield* this.step(); + } + *flowCollection(fc) { + const it = fc.items[fc.items.length - 1]; + if (this.type === "flow-error-end") { + let top; + do { + yield* this.pop(); + top = this.peek(1); + } while (top?.type === "flow-collection"); + } else if (fc.end.length === 0) { + switch (this.type) { + case "comma": + case "explicit-key-ind": + if (!it || it.sep) + fc.items.push({ start: [this.sourceToken] }); + else + it.start.push(this.sourceToken); + return; + case "map-value-ind": + if (!it || it.value) + fc.items.push({ start: [], key: null, sep: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + Object.assign(it, { key: null, sep: [this.sourceToken] }); + return; + case "space": + case "comment": + case "newline": + case "anchor": + case "tag": + if (!it || it.value) + fc.items.push({ start: [this.sourceToken] }); + else if (it.sep) + it.sep.push(this.sourceToken); + else + it.start.push(this.sourceToken); + return; + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": { + const fs14 = this.flowScalar(this.type); + if (!it || it.value) + fc.items.push({ start: [], key: fs14, sep: [] }); + else if (it.sep) + this.stack.push(fs14); + else + Object.assign(it, { key: fs14, sep: [] }); + return; + } + case "flow-map-end": + case "flow-seq-end": + fc.end.push(this.sourceToken); + return; + } + const bv = this.startBlockValue(fc); + if (bv) + this.stack.push(bv); + else { + yield* this.pop(); + yield* this.step(); + } + } else { + const parent = this.peek(2); + if (parent.type === "block-map" && (this.type === "map-value-ind" && parent.indent === fc.indent || this.type === "newline" && !parent.items[parent.items.length - 1].sep)) { + yield* this.pop(); + yield* this.step(); + } else if (this.type === "map-value-ind" && parent.type !== "flow-collection") { + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + fixFlowSeqItems(fc); + const sep = fc.end.splice(1, fc.end.length); + sep.push(this.sourceToken); + const map = { + type: "block-map", + offset: fc.offset, + indent: fc.indent, + items: [{ start, key: fc, sep }] + }; + this.onKeyLine = true; + this.stack[this.stack.length - 1] = map; + } else { + yield* this.lineEnd(fc); + } + } + } + flowScalar(type) { + if (this.onNewLine) { + let nl = this.source.indexOf("\n") + 1; + while (nl !== 0) { + this.onNewLine(this.offset + nl); + nl = this.source.indexOf("\n", nl) + 1; + } + } + return { + type, + offset: this.offset, + indent: this.indent, + source: this.source + }; + } + startBlockValue(parent) { + switch (this.type) { + case "alias": + case "scalar": + case "single-quoted-scalar": + case "double-quoted-scalar": + return this.flowScalar(this.type); + case "block-scalar-header": + return { + type: "block-scalar", + offset: this.offset, + indent: this.indent, + props: [this.sourceToken], + source: "" + }; + case "flow-map-start": + case "flow-seq-start": + return { + type: "flow-collection", + offset: this.offset, + indent: this.indent, + start: this.sourceToken, + items: [], + end: [] + }; + case "seq-item-ind": + return { + type: "block-seq", + offset: this.offset, + indent: this.indent, + items: [{ start: [this.sourceToken] }] + }; + case "explicit-key-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + start.push(this.sourceToken); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, explicitKey: true }] + }; + } + case "map-value-ind": { + this.onKeyLine = true; + const prev = getPrevProps(parent); + const start = getFirstKeyStartProps(prev); + return { + type: "block-map", + offset: this.offset, + indent: this.indent, + items: [{ start, key: null, sep: [this.sourceToken] }] + }; + } + } + return null; + } + atIndentedComment(start, indent) { + if (this.type !== "comment") + return false; + if (this.indent <= indent) + return false; + return start.every((st) => st.type === "newline" || st.type === "space"); + } + *documentEnd(docEnd) { + if (this.type !== "doc-mode") { + if (docEnd.end) + docEnd.end.push(this.sourceToken); + else + docEnd.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + *lineEnd(token) { + switch (this.type) { + case "comma": + case "doc-start": + case "doc-end": + case "flow-seq-end": + case "flow-map-end": + case "map-value-ind": + yield* this.pop(); + yield* this.step(); + break; + case "newline": + this.onKeyLine = false; + // fallthrough + case "space": + case "comment": + default: + if (token.end) + token.end.push(this.sourceToken); + else + token.end = [this.sourceToken]; + if (this.type === "newline") + yield* this.pop(); + } + } + }; + exports.Parser = Parser; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js +var require_public_api = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/public-api.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document2 = require_Document(); + var errors = require_errors(); + var log = require_log(); + var identity = require_identity(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + function parseOptions(options) { + const prettyErrors = options.prettyErrors !== false; + const lineCounter$1 = options.lineCounter || prettyErrors && new lineCounter.LineCounter() || null; + return { lineCounter: lineCounter$1, prettyErrors }; + } + function parseAllDocuments(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + const docs = Array.from(composer$1.compose(parser$1.parse(source))); + if (prettyErrors && lineCounter2) + for (const doc of docs) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + if (docs.length > 0) + return docs; + return Object.assign([], { empty: true }, composer$1.streamInfo()); + } + function parseDocument6(source, options = {}) { + const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options); + const parser$1 = new parser.Parser(lineCounter2?.addNewLine); + const composer$1 = new composer.Composer(options); + let doc = null; + for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) { + if (!doc) + doc = _doc; + else if (doc.options.logLevel !== "silent") { + doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), "MULTIPLE_DOCS", "Source contains multiple documents; please use YAML.parseAllDocuments()")); + break; + } + } + if (prettyErrors && lineCounter2) { + doc.errors.forEach(errors.prettifyError(source, lineCounter2)); + doc.warnings.forEach(errors.prettifyError(source, lineCounter2)); + } + return doc; + } + function parse(src, reviver, options) { + let _reviver = void 0; + if (typeof reviver === "function") { + _reviver = reviver; + } else if (options === void 0 && reviver && typeof reviver === "object") { + options = reviver; + } + const doc = parseDocument6(src, options); + if (!doc) + return null; + doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning)); + if (doc.errors.length > 0) { + if (doc.options.logLevel !== "silent") + throw doc.errors[0]; + else + doc.errors = []; + } + return doc.toJS(Object.assign({ reviver: _reviver }, options)); + } + function stringify3(value, replacer, options) { + let _replacer = null; + if (typeof replacer === "function" || Array.isArray(replacer)) { + _replacer = replacer; + } else if (options === void 0 && replacer) { + options = replacer; + } + if (typeof options === "string") + options = options.length; + if (typeof options === "number") { + const indent = Math.round(options); + options = indent < 1 ? void 0 : indent > 8 ? { indent: 8 } : { indent }; + } + if (value === void 0) { + const { keepUndefined } = options ?? replacer ?? {}; + if (!keepUndefined) + return void 0; + } + if (identity.isDocument(value) && !_replacer) + return value.toString(options); + return new Document2.Document(value, _replacer, options).toString(options); + } + exports.parse = parse; + exports.parseAllDocuments = parseAllDocuments; + exports.parseDocument = parseDocument6; + exports.stringify = stringify3; + } +}); + +// node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js +var require_dist = __commonJS({ + "node_modules/.pnpm/yaml@2.9.0/node_modules/yaml/dist/index.js"(exports) { + "use strict"; + var composer = require_composer(); + var Document2 = require_Document(); + var Schema = require_Schema(); + var errors = require_errors(); + var Alias = require_Alias(); + var identity = require_identity(); + var Pair = require_Pair(); + var Scalar = require_Scalar(); + var YAMLMap = require_YAMLMap(); + var YAMLSeq = require_YAMLSeq(); + var cst = require_cst(); + var lexer = require_lexer(); + var lineCounter = require_line_counter(); + var parser = require_parser(); + var publicApi = require_public_api(); + var visit = require_visit(); + exports.Composer = composer.Composer; + exports.Document = Document2.Document; + exports.Schema = Schema.Schema; + exports.YAMLError = errors.YAMLError; + exports.YAMLParseError = errors.YAMLParseError; + exports.YAMLWarning = errors.YAMLWarning; + exports.Alias = Alias.Alias; + exports.isAlias = identity.isAlias; + exports.isCollection = identity.isCollection; + exports.isDocument = identity.isDocument; + exports.isMap = identity.isMap; + exports.isNode = identity.isNode; + exports.isPair = identity.isPair; + exports.isScalar = identity.isScalar; + exports.isSeq = identity.isSeq; + exports.Pair = Pair.Pair; + exports.Scalar = Scalar.Scalar; + exports.YAMLMap = YAMLMap.YAMLMap; + exports.YAMLSeq = YAMLSeq.YAMLSeq; + exports.CST = cst; + exports.Lexer = lexer.Lexer; + exports.LineCounter = lineCounter.LineCounter; + exports.Parser = parser.Parser; + exports.parse = publicApi.parse; + exports.parseAllDocuments = publicApi.parseAllDocuments; + exports.parseDocument = publicApi.parseDocument; + exports.stringify = publicApi.stringify; + exports.visit = visit.visit; + exports.visitAsync = visit.visitAsync; + } +}); + +// domains/engine/state.ts +var state_exports = {}; +__export(state_exports, { + RUN_STATE_FILE: () => RUN_STATE_FILE, + applyRunStateToDocument: () => applyRunStateToDocument, + readRunState: () => readRunState, + removeRunState: () => removeRunState, + runStateFromDocument: () => runStateFromDocument, + writeRunState: () => writeRunState +}); +import { randomUUID as randomUUID2 } from "crypto"; +import { promises as fs4 } from "fs"; +import path4 from "path"; +function requiredString(doc, key) { + const value = doc[key]; + if (typeof value !== "string" || value.length === 0) { + throw new Error(`Invalid Run state: ${key} must be a non-empty string`); + } + return value; +} +function requiredRunReference(doc, key) { + const value = requiredString(doc, key); + if (path4.isAbsolute(value) || /^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Run state: ${key} must stay inside the change directory`); + } + return value; +} +function retries(doc) { + const raw = doc.run_retries ?? "{}"; + let value; + try { + value = typeof raw === "string" ? JSON.parse(raw) : raw; + } catch (error) { + throw new Error("Invalid Run state: run_retries must be a JSON object", { cause: error }); + } + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Run state: run_retries must be a JSON object"); + } + for (const count of Object.values(value)) { + if (!Number.isInteger(count) || Number(count) < 0) { + throw new Error("Invalid Run state: retry counts must be non-negative integers"); + } + } + return value; +} +function runStateFromDocument(doc) { + if (!doc.run_id) return null; + const runId = requiredString(doc, "run_id"); + const skill = requiredString(doc, "skill"); + const skillVersion = requiredString(doc, "skill_version"); + const skillHash = requiredString(doc, "skill_hash"); + const pendingRef = requiredRunReference(doc, "pending_ref"); + const trajectoryRef = requiredRunReference(doc, "trajectory_ref"); + const contextRef = requiredRunReference(doc, "context_ref"); + const artifactsRef = requiredRunReference(doc, "artifacts_ref"); + const checkpointRef = requiredRunReference(doc, "checkpoint_ref"); + const iteration = Number(doc.iteration); + if (!Number.isInteger(iteration) || iteration < 0) { + throw new Error("Invalid Run state: iteration must be a non-negative integer"); + } + if (doc.orchestration !== "deterministic" && doc.orchestration !== "adaptive") { + throw new Error("Invalid Run state: orchestration must be deterministic or adaptive"); + } + if (doc.run_status !== "running" && doc.run_status !== "waiting" && doc.run_status !== "completed" && doc.run_status !== "failed") { + throw new Error("Invalid Run state: run_status is invalid"); + } + return { + runId, + skill, + skillVersion, + skillHash, + orchestration: doc.orchestration, + currentStep: field(doc, "current_step"), + iteration, + pending: field(doc, "pending"), + pendingRef, + trajectoryRef, + contextRef, + artifactsRef, + checkpointRef, + status: doc.run_status, + retries: retries(doc) + }; +} +function applyRunStateToDocument(doc, state) { + if (state) { + doc.run_id = state.runId; + } else { + delete doc.run_id; + } +} +function runStateToJson(state) { + return { + runId: state.runId, + skill: state.skill, + skillVersion: state.skillVersion, + skillHash: state.skillHash, + orchestration: state.orchestration, + currentStep: state.currentStep, + iteration: state.iteration, + pending: state.pending, + pendingRef: state.pendingRef, + trajectoryRef: state.trajectoryRef, + contextRef: state.contextRef, + artifactsRef: state.artifactsRef, + checkpointRef: state.checkpointRef, + status: state.status, + retries: state.retries + }; +} +function runStateFromJson(json) { + const doc = { + run_id: json.runId, + skill: json.skill, + skill_version: json.skillVersion, + skill_hash: json.skillHash, + orchestration: json.orchestration, + current_step: json.currentStep, + iteration: json.iteration, + pending: json.pending, + pending_ref: json.pendingRef, + trajectory_ref: json.trajectoryRef, + context_ref: json.contextRef, + artifacts_ref: json.artifactsRef, + checkpoint_ref: json.checkpointRef, + run_status: json.status, + run_retries: JSON.stringify(json.retries) + }; + return runStateFromDocument(doc); +} +async function readRunState(changeDir) { + const file = path4.join(changeDir, RUN_STATE_FILE); + let raw; + try { + raw = await fs4.readFile(file, "utf8"); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const json = JSON.parse(raw); + return runStateFromJson(json); +} +async function writeRunState(changeDir, state) { + await fs4.mkdir(path4.join(changeDir, ".comet"), { recursive: true }); + const file = path4.join(changeDir, RUN_STATE_FILE); + const temporary = path4.join(changeDir, ".comet", `run-state.${randomUUID2()}.tmp`); + await fs4.writeFile(temporary, JSON.stringify(runStateToJson(state), null, 2), "utf8"); + await fs4.rename(temporary, file); +} +async function removeRunState(changeDir) { + await fs4.rm(path4.join(changeDir, RUN_STATE_FILE), { force: true }); +} +var field, RUN_STATE_FILE; +var init_state = __esm({ + "domains/engine/state.ts"() { + "use strict"; + field = (doc, key) => { + const value = doc[key]; + return value === null || value === void 0 ? null : String(value); + }; + RUN_STATE_FILE = ".comet/run-state.json"; + } +}); + +// domains/comet-entry/hook-router-entry.ts +import path13 from "path"; +import { promises as fs13 } from "fs"; + +// domains/comet-native/native-paths.ts +import { promises as fs } from "fs"; +import path from "path"; +import os from "os"; +var PROJECT_CONFIG_FILE = ".comet/config.yaml"; +async function isFileOrDirectory(target) { + try { + await fs.lstat(target); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function declaresNativeProjectConfig(target) { + try { + const source = await fs.readFile(target, "utf8"); + return /^schema:\s*comet\.project\.v1\s*$/mu.test(source); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +function inside(parent, target) { + const relative = path.relative(parent, target); + return relative === "" || !path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`); +} +async function physicalPath(target) { + const missing = []; + let cursor = target; + while (!await isFileOrDirectory(cursor)) { + const parent = path.dirname(cursor); + if (parent === cursor) break; + missing.push(path.basename(cursor)); + cursor = parent; + } + const existing = await fs.realpath(cursor); + return path.resolve(existing, ...missing.reverse()); +} +async function isSymbolicLink(target) { + try { + return (await fs.lstat(target)).isSymbolicLink(); + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +async function discoverNativeProject(startPath) { + let cursor = path.resolve(startPath); + try { + if (!(await fs.stat(cursor)).isDirectory()) cursor = path.dirname(cursor); + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const fallback = cursor; + const home = path.resolve(os.homedir()); + while (true) { + const isHomeBoundary = cursor === home && fallback !== home; + if (!isHomeBoundary) { + const configFile = path.join(cursor, ...PROJECT_CONFIG_FILE.split("/")); + const configMarksProject = cursor === fallback || await declaresNativeProjectConfig(configFile); + if (await isFileOrDirectory(configFile) && configMarksProject) { + return cursor; + } + } + if (await isFileOrDirectory(path.join(cursor, ".git"))) return cursor; + const parent = path.dirname(cursor); + if (parent === cursor) return fallback; + cursor = parent; + } +} +function normalizeArtifactRootRef(value) { + const trimmed = value.trim(); + if (trimmed.length === 0 || path.isAbsolute(trimmed) || /^(?:[A-Za-z]:|~|[\\/])/u.test(trimmed)) { + throw new Error("native.artifact_root must be a project-relative path"); + } + const segments = trimmed.replaceAll("\\", "/").split("/"); + if (segments.includes("..")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + const normalized2 = path.posix.normalize(segments.filter((segment) => segment !== "").join("/")); + if (normalized2 === ".." || normalized2.startsWith("../")) { + throw new Error("native.artifact_root must stay inside the project root"); + } + return normalized2 === "" ? "." : normalized2; +} +async function resolveArtifactRoot(projectRoot, value) { + const normalized2 = normalizeArtifactRootRef(value); + const lexical = path.resolve(projectRoot, ...normalized2.split("/")); + const physicalProject = await fs.realpath(projectRoot); + const physicalTarget = await physicalPath(lexical); + if (!inside(physicalProject, physicalTarget)) { + throw new Error("native.artifact_root resolves outside the project root"); + } + return lexical; +} +async function nativeProjectPaths(projectRoot, artifactRootRef) { + const normalized2 = normalizeArtifactRootRef(artifactRootRef); + const artifactRoot = await resolveArtifactRoot(projectRoot, normalized2); + const nativeRoot = path.join(artifactRoot, "comet"); + if (await isSymbolicLink(nativeRoot)) { + throw new Error("The configured Native comet root must not be a symbolic link"); + } + const [physicalArtifactRoot, physicalNativeRoot] = await Promise.all([ + physicalPath(artifactRoot), + physicalPath(nativeRoot) + ]); + if (!inside(physicalArtifactRoot, physicalNativeRoot)) { + throw new Error("The configured Native comet root resolves outside its artifact root"); + } + return { + projectRoot: path.resolve(projectRoot), + configFile: path.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")), + artifactRoot, + artifactRootRef: normalized2, + nativeRoot, + specsDir: path.join(nativeRoot, "specs"), + changesDir: path.join(nativeRoot, "changes"), + archiveDir: path.join(nativeRoot, "archive"), + runtimeDir: path.join(nativeRoot, "runtime"), + locksDir: path.join(nativeRoot, "runtime", "locks"), + transactionsDir: path.join(nativeRoot, "runtime", "transactions") + }; +} +function isInsidePath(parent, target) { + return inside(path.resolve(parent), path.resolve(target)); +} +async function resolveContainedNativePath(root, target) { + const lexicalRoot = path.resolve(root); + const lexicalTarget = path.resolve(target); + if (!inside(lexicalRoot, lexicalTarget)) { + throw new Error(`Path is outside the Native root: ${target}`); + } + if (await isSymbolicLink(lexicalRoot)) { + throw new Error(`Native root must not be a symbolic link: ${root}`); + } + const [physicalRoot, physicalTarget] = await Promise.all([ + physicalPath(lexicalRoot), + physicalPath(lexicalTarget) + ]); + if (!inside(physicalRoot, physicalTarget)) { + throw new Error(`Path resolves outside the Native root: ${target}`); + } + return lexicalTarget; +} + +// domains/comet-entry/hook-adapter.ts +import { readFileSync } from "fs"; +var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "applypatch", + "create", + "createfile", + "deletefile", + "edit", + "editfile", + "patch", + "strreplaceeditor", + "write", + "writefile", + "writefiletool" +]); +var NON_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([ + "glob", + "grep", + "listfiles", + "read", + "readfile", + "search", + "view" +]); +var SINGULAR_PATH_KEYS = ["file_path", "filePath", "path", "target_file", "targetFile"]; +var PLURAL_PATH_KEYS = ["file_paths", "filePaths", "paths", "files", "targets"]; +var NESTED_TARGET_KEYS = ["operations", "edits"]; +var PATCH_KEYS = ["patch", "diff", "patchText", "patch_text", "changes"]; +var COMET_HOOK_PLATFORM_IDS = /* @__PURE__ */ new Set([ + "claude", + "codex", + "windsurf", + "github-copilot", + "gemini", + "amazon-q", + "qwen", + "kiro", + "codebuddy", + "qoder" +]); +function isRecord(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function normalizedToolName(value) { + return value.toLowerCase().replace(/[^a-z0-9]+/gu, ""); +} +function readToolName(input) { + for (const key of ["tool_name", "toolName", "tool", "name"]) { + const value = input[key]; + if (typeof value === "string" && value.trim()) return value.trim(); + } + return null; +} +function parseJsonValue(value) { + if (typeof value !== "string") return value; + const source = value.trim(); + if (!source.startsWith("{") && !source.startsWith("[")) return value; + try { + return JSON.parse(source); + } catch { + return value; + } +} +function readToolArguments(input) { + for (const key of ["tool_input", "toolInput", "toolArgs", "tool_args", "arguments"]) { + if (input[key] !== void 0) return parseJsonValue(input[key]); + } + return input; +} +function patchTargets(source) { + const targets = []; + const patterns = [ + /^\*\*\* (?:Add|Update|Delete) File:\s+(.+?)\s*$/gmu, + /^\+\+\+\s+(?:b\/)?(.+?)\s*$/gmu + ]; + for (const pattern of patterns) { + for (const match of source.matchAll(pattern)) { + const target = match[1]?.trim(); + if (target && target !== "/dev/null") targets.push(target); + } + } + return targets; +} +function addTarget(targets, value) { + if (typeof value === "string") { + const target = value.trim(); + if (target) targets.push(target); + return; + } + if (Array.isArray(value)) { + for (const entry of value) addTarget(targets, entry); + return; + } + if (!isRecord(value)) return; + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, value[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, value[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, value[key]); +} +function collectTargets(input, args) { + const targets = []; + const records = [args, input].filter(isRecord); + for (const record3 of records) { + for (const key of SINGULAR_PATH_KEYS) addTarget(targets, record3[key]); + for (const key of PLURAL_PATH_KEYS) addTarget(targets, record3[key]); + for (const key of NESTED_TARGET_KEYS) addTarget(targets, record3[key]); + for (const key of PATCH_KEYS) { + const value = record3[key]; + if (typeof value === "string") targets.push(...patchTargets(value)); + } + } + if (typeof args === "string") targets.push(...patchTargets(args)); + return [...new Set(targets)]; +} +function parseCometHookRequest(source, filePath) { + if (filePath?.trim()) { + return { intent: "write", targets: [filePath.trim()], toolName: null }; + } + if (!source.trim()) return { intent: "unknown", targets: [], toolName: null }; + let input; + try { + input = JSON.parse(source); + } catch { + return { intent: "unknown", targets: [], toolName: null }; + } + if (!isRecord(input)) return { intent: "unknown", targets: [], toolName: null }; + const toolName = readToolName(input); + const targets = collectTargets(input, readToolArguments(input)); + if (toolName && WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName + }; + } + if (toolName && NON_WRITE_TOOL_NAMES.has(normalizedToolName(toolName))) { + return { intent: "non-write", targets: [], toolName }; + } + if (toolName) return { intent: "unknown", targets, toolName }; + return { + intent: targets.length > 0 ? "write" : "unknown", + targets, + toolName: null + }; +} +function readCometHookRequest() { + const filePath = process.env.FILE_PATH; + if (filePath?.trim()) return parseCometHookRequest("", filePath); + if (process.stdin.isTTY) return parseCometHookRequest("", filePath); + try { + return parseCometHookRequest(readFileSync(0, "utf8"), filePath); + } catch { + return parseCometHookRequest("", filePath); + } +} +function renderCometHookDecision(platformId, decision) { + if (!COMET_HOOK_PLATFORM_IDS.has(platformId)) { + return { + exitCode: 64, + stdout: "", + stderr: `Unsupported Comet Hook platform: ${platformId} +` + }; + } + if (platformId === "github-copilot") { + return { + exitCode: 0, + stdout: decision.allowed ? "{}\n" : `${JSON.stringify({ + permissionDecision: "deny", + permissionDecisionReason: decision.reason + })} +`, + stderr: "" + }; + } + if (decision.allowed) return { exitCode: 0, stdout: "", stderr: "" }; + return { exitCode: 2, stdout: "", stderr: `${decision.reason} +` }; +} + +// domains/comet-classic/classic-hook-guard.ts +import { existsSync, promises as fs7, readFileSync as readFileSync2 } from "fs"; +import path7 from "path"; + +// domains/comet-classic/classic-branch-binding.ts +var import_yaml = __toESM(require_dist(), 1); +import { execFileSync } from "child_process"; +import { randomUUID } from "crypto"; +import { promises as fs2 } from "fs"; +import path2 from "path"; +function liveGitBranch(cwd) { + try { + const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"] + }).trim(); + return branch && branch !== "HEAD" ? branch : null; + } catch { + return null; + } +} +function isGitWorkTree(cwd) { + try { + return execFileSync("git", ["rev-parse", "--is-inside-work-tree"], { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"] + }).trim() === "true"; + } catch { + return false; + } +} +var BOUND_BRANCH_ISOLATIONS = ["current", "branch", "worktree"]; +function requiresBranchBinding(isolation) { + return BOUND_BRANCH_ISOLATIONS.includes(isolation); +} +function evaluateBranchBinding(input) { + if (!requiresBranchBinding(input.isolation)) return { status: "not-applicable" }; + if (input.boundBranch === null && input.currentBranch === null && input.gitWorkTree === false) { + return { status: "not-applicable" }; + } + if (input.boundBranch === null) { + return input.currentBranch === null ? { status: "unbound-detached" } : { status: "needs-heal", branch: input.currentBranch }; + } + if (input.currentBranch === input.boundBranch) return { status: "ok" }; + return { status: "drift", boundBranch: input.boundBranch, currentBranch: input.currentBranch }; +} +async function resolveBranchBinding(changeDir, options) { + const file = path2.join(changeDir, ".comet.yaml"); + const document = (0, import_yaml.parseDocument)(await fs2.readFile(file, "utf8"), { uniqueKeys: false }); + if (document.errors.length > 0) { + throw new Error(`Invalid .comet.yaml: ${document.errors[0].message}`); + } + const record3 = document.toJS() ?? {}; + const isolation = typeof record3.isolation === "string" ? record3.isolation : null; + const boundBranch = typeof record3.bound_branch === "string" && record3.bound_branch !== "" ? record3.bound_branch : null; + const bindingRequired = requiresBranchBinding(isolation); + const currentBranch = liveGitBranch(options.cwd); + const gitWorkTree = bindingRequired && boundBranch === null && currentBranch === null ? isGitWorkTree(options.cwd) : true; + const verdict = evaluateBranchBinding({ isolation, boundBranch, currentBranch, gitWorkTree }); + if (verdict.status === "needs-heal" && options.heal) { + await healBoundBranch(changeDir, verdict.branch); + return { status: "healed", branch: verdict.branch, bindingRequired, currentBranch }; + } + return { ...verdict, bindingRequired, currentBranch }; +} +async function healBoundBranch(changeDir, branch) { + const file = path2.join(changeDir, ".comet.yaml"); + const document = (0, import_yaml.parseDocument)(await fs2.readFile(file, "utf8"), { uniqueKeys: false }); + document.set("bound_branch", branch); + const temporary = `${file}.${randomUUID()}.tmp`; + try { + await fs2.writeFile(temporary, document.toString(), "utf8"); + await fs2.rename(temporary, file); + } catch (error) { + await fs2.rm(temporary, { force: true }); + throw error; + } +} +function branchLabel(currentBranch) { + return currentBranch ?? "detached HEAD"; +} +function driftStaleReason(change, boundBranch, currentBranch) { + return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'`; +} +function unboundDetachedMessage(change) { + return `change '${change}' uses a branch-bound workspace mode but has no bound branch and HEAD is detached; checkout a branch first before continuing.`; +} + +// domains/comet-classic/classic-current-change.ts +import { promises as fs6 } from "fs"; +import path6 from "path"; + +// domains/comet-entry/current-selection.ts +import path3 from "path"; + +// platform/fs/race-safe-read.ts +import { constants as fsConstants, promises as fs3 } from "fs"; + +// platform/fs/file-identity.ts +function hasPlatformIdentity(value) { + return value !== 0 && value !== 0n && value !== "0"; +} +function hasComparableFileObject(left, right) { + return hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev) && hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); +} +function sameFileObject(left, right) { + const comparableDevice = hasPlatformIdentity(left.dev) && hasPlatformIdentity(right.dev); + if (comparableDevice && left.dev !== right.dev) return false; + const comparableInode = hasPlatformIdentity(left.ino) && hasPlatformIdentity(right.ino); + if (comparableInode && left.ino !== right.ino) return false; + if (comparableDevice && comparableInode) return true; + return left.birthtime === right.birthtime; +} + +// platform/fs/race-safe-read.ts +var RaceSafeReadError = class extends Error { + reason; + constructor(reason, message, options) { + super(message, options); + this.name = "RaceSafeReadError"; + this.reason = reason; + } +}; +function birthtimeOf(stat) { + return "birthtimeNs" in stat && typeof stat.birthtimeNs === "bigint" ? stat.birthtimeNs : stat.birthtimeMs; +} +function ctimeOf(stat) { + return "ctimeNs" in stat && typeof stat.ctimeNs === "bigint" ? stat.ctimeNs : stat.ctimeMs; +} +function identityOf(stat) { + return { dev: stat.dev, ino: stat.ino, birthtime: birthtimeOf(stat) }; +} +function sameStatIdentity(left, right) { + const leftObject = identityOf(left); + const rightObject = identityOf(right); + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && birthtimeOf(left) === birthtimeOf(right) && ctimeOf(left) === ctimeOf(right) && left.size === right.size; +} +async function readFileRaceSafe(file, maxBytes, options = {}) { + if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) { + throw new Error("race-safe read byte limit must be a positive integer"); + } + const label = options.label ?? "file"; + const bigint = options.bigint === true; + const before = await fs3.lstat(file, { bigint }); + if (!before.isFile() || before.isSymbolicLink()) { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`); + } + if (BigInt(before.size) > BigInt(maxBytes)) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + const beforeRealPath = await fs3.realpath(file); + await options.verify?.("pre-open", { realPath: beforeRealPath, identity: identityOf(before) }); + const flags = process.platform === "win32" ? fsConstants.O_RDONLY : fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK; + let handle; + try { + handle = await fs3.open(file, flags); + } catch (error) { + if (error.code === "ELOOP") { + throw new RaceSafeReadError("not-regular-file", `${label} must be a regular file`, { + cause: error + }); + } + throw error; + } + try { + const [opened, pathAfterOpen, realPathAfterOpen] = await Promise.all([ + handle.stat({ bigint }), + fs3.lstat(file, { bigint }), + fs3.realpath(file) + ]); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameStatIdentity(before, opened) || !sameStatIdentity(before, pathAfterOpen)) { + throw new RaceSafeReadError("changed", `${label} changed while opening`); + } + await options.verify?.("post-open", { + realPath: realPathAfterOpen, + identity: identityOf(opened) + }); + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + for (; ; ) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) { + throw new RaceSafeReadError("too-large", `${label} exceeds ${maxBytes} bytes`); + } + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat({ bigint }), + fs3.lstat(file, { bigint }), + fs3.realpath(file) + ]); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameStatIdentity(before, afterHandle) || !sameStatIdentity(before, afterPath)) { + throw new RaceSafeReadError("changed", `${label} changed while reading`); + } + await options.verify?.("post-read", { + realPath: afterRealPath, + identity: identityOf(afterHandle) + }); + return { bytes: Buffer.concat(chunks, total), stat: afterHandle, realPath: afterRealPath }; + } finally { + await handle.close(); + } +} + +// domains/comet-entry/current-selection.ts +var COMET_CURRENT_SELECTION_SCHEMA = "comet.selection.v2"; +var COMET_CURRENT_SELECTION_MAX_BYTES = 16 * 1024; +function cometCurrentSelectionFile(projectRoot) { + return path3.join(projectRoot, ".comet", "current-change.json"); +} +function isRecord2(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} +function validBranch(value) { + return value === null || typeof value === "string"; +} +function parseSelection(source) { + let value; + try { + value = JSON.parse(source); + } catch (error) { + throw new Error( + `current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + if (!isRecord2(value)) { + throw new Error("current change selection must be a JSON object"); + } + if (value.version === 1) { + if (typeof value.change !== "string") { + throw new Error("legacy current change selection change must be a string"); + } + if (value.branch !== void 0 && !validBranch(value.branch)) { + throw new Error("legacy current change selection branch must be a string or null"); + } + return { + selection: { + schema: COMET_CURRENT_SELECTION_SCHEMA, + workflow: "classic", + change: value.change, + branch: value.branch ?? null + }, + legacy: true + }; + } + if (value.schema !== COMET_CURRENT_SELECTION_SCHEMA) { + throw new Error(`current change selection schema must be ${COMET_CURRENT_SELECTION_SCHEMA}`); + } + if (value.workflow !== "native" && value.workflow !== "classic") { + throw new Error("current change selection workflow must be native or classic"); + } + if (typeof value.change !== "string") { + throw new Error("current change selection change must be a string"); + } + if (!validBranch(value.branch)) { + throw new Error("current change selection branch must be a string or null"); + } + if (value.workflow === "native" && value.branch !== null) { + throw new Error("Native current change selection branch must be null"); + } + return { selection: value, legacy: false }; +} +async function readCometCurrentSelection(projectRoot) { + let source; + try { + const file = cometCurrentSelectionFile(projectRoot); + const { bytes } = await readFileRaceSafe(file, COMET_CURRENT_SELECTION_MAX_BYTES, { + label: "current change selection" + }); + source = bytes.toString("utf8"); + } catch (error) { + if (error.code === "ENOENT") return { status: "missing" }; + throw new Error( + `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`, + { cause: error } + ); + } + const parsed = parseSelection(source); + return { status: "selected", ...parsed }; +} + +// domains/comet-classic/classic-paths.ts +function openSpecChangeNameError(name) { + if (!name) return "Change name cannot be empty"; + if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(name)) { + return `Invalid change name: '${name}' +Valid format: lowercase kebab-case (a-z, 0-9, single hyphens)`; + } + if (name.includes("..")) return "Change name cannot contain '..' (path traversal not allowed)"; + return null; +} +function assertOpenSpecChangeName(name) { + const error = openSpecChangeNameError(name); + if (error) throw new Error(error); +} + +// domains/comet-classic/classic-store.ts +var import_yaml2 = __toESM(require_dist(), 1); +import { randomUUID as randomUUID3 } from "crypto"; +import { promises as fs5 } from "fs"; +import path5 from "path"; + +// domains/comet-classic/classic-state.ts +init_state(); +var CLASSIC_PROFILES = ["full", "hotfix", "tweak"]; +var CLASSIC_MIGRATION_VERSION = 1; +var PHASES = ["open", "design", "build", "verify", "archive"]; +var ARTIFACT_LANGUAGES = ["en", "zh-CN"]; +var CONTEXT_COMPRESSION = ["off", "beta"]; +var BUILD_MODES = ["subagent-driven-development", "executing-plans", "direct"]; +var BUILD_PAUSES = ["plan-ready"]; +var SUBAGENT_DISPATCH = ["confirmed"]; +var TDD_MODES = ["tdd", "direct"]; +var REVIEW_MODES = ["off", "standard", "thorough"]; +var ISOLATIONS = ["current", "branch", "worktree"]; +var VERIFY_MODES = ["light", "full"]; +var VERIFY_RESULTS = ["pending", "pass", "fail"]; +var BRANCH_STATUSES = ["pending", "handled"]; +var ARCHIVE_CONFIRMATIONS = ["pending", "confirmed"]; +var CLASSIC_WIRE_KEYS = [ + "workflow", + "language", + "phase", + "context_compression", + "build_mode", + "build_pause", + "subagent_dispatch", + "tdd_mode", + "review_mode", + "isolation", + "bound_branch", + "verify_mode", + "auto_transition", + "base_ref", + "design_doc", + "plan", + "verify_result", + "verify_failures", + "verification_report", + "branch_status", + "created_at", + "verified_at", + "archive_confirmation", + "archived", + "direct_override", + "handoff_context", + "handoff_hash", + "classic_profile", + "classic_migration" +]; +var RUN_WIRE_KEYS = ["run_id"]; +var KNOWN_KEYS = /* @__PURE__ */ new Set([...CLASSIC_WIRE_KEYS, ...RUN_WIRE_KEYS]); +var REQUIRED_CLASSIC_KEYS = [ + "workflow", + "phase", + "design_doc", + "plan", + "build_mode", + "isolation", + "verify_mode", + "verify_result", + "verified_at", + "archived" +]; +function has(doc, key) { + return Object.prototype.hasOwnProperty.call(doc, key); +} +function nullableString(doc, key) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") return null; + if (typeof value !== "string") { + throw new Error(`Invalid Classic state: ${key} must be a string or null`); + } + return value; +} +function enumValue(doc, key, values, nullable = true) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") { + if (nullable) return null; + throw new Error(`Invalid Classic state: ${key} is required`); + } + if (typeof value !== "string" || !values.includes(value)) { + throw new Error( + `Invalid Classic state: ${key} must be one of ${values.join(", ")}${nullable ? " or null" : ""}` + ); + } + return value; +} +function booleanValue(doc, key, nullable = true) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") { + if (nullable) return null; + throw new Error(`Invalid Classic state: ${key} is required`); + } + if (typeof value !== "boolean") { + throw new Error(`Invalid Classic state: ${key} must be true or false`); + } + return value; +} +function nonNegativeInteger(doc, key, fallback = 0) { + const value = doc[key]; + if (value === null || value === void 0 || value === "") return fallback; + if (typeof value !== "number" || !Number.isInteger(value) || value < 0) { + throw new Error(`Invalid Classic state: ${key} must be a non-negative integer`); + } + return value; +} +function relativePath(doc, key) { + const value = nullableString(doc, key); + if (value === null) return null; + if (/^(?:[A-Za-z]:|[\\/]|~)/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`Invalid Classic state: ${key} must be a relative repository path`); + } + return value; +} +function sha256(doc, key) { + const value = nullableString(doc, key); + if (value !== null && !/^[a-f0-9]{64}$/u.test(value)) { + throw new Error(`Invalid Classic state: ${key} must be a sha256 hex digest`); + } + return value; +} +function migrationVersion(doc) { + const value = doc.classic_migration; + if (value === null || value === void 0 || value === "") return null; + if (value !== CLASSIC_MIGRATION_VERSION) { + throw new Error( + `Invalid Classic state: classic_migration must be ${CLASSIC_MIGRATION_VERSION}` + ); + } + return value; +} +function classicStateFromDocument(doc) { + const hasClassicProjection = CLASSIC_WIRE_KEYS.some((key) => has(doc, key)); + if (!hasClassicProjection) return null; + for (const key of REQUIRED_CLASSIC_KEYS) { + if (!has(doc, key)) return null; + } + return { + workflow: enumValue(doc, "workflow", CLASSIC_PROFILES, false), + language: enumValue(doc, "language", ARTIFACT_LANGUAGES), + phase: enumValue(doc, "phase", PHASES, false), + contextCompression: enumValue(doc, "context_compression", CONTEXT_COMPRESSION), + buildMode: enumValue(doc, "build_mode", BUILD_MODES), + buildPause: enumValue(doc, "build_pause", BUILD_PAUSES), + subagentDispatch: enumValue(doc, "subagent_dispatch", SUBAGENT_DISPATCH), + tddMode: enumValue(doc, "tdd_mode", TDD_MODES), + reviewMode: enumValue(doc, "review_mode", REVIEW_MODES), + isolation: enumValue(doc, "isolation", ISOLATIONS), + boundBranch: nullableString(doc, "bound_branch"), + verifyMode: enumValue(doc, "verify_mode", VERIFY_MODES), + autoTransition: booleanValue(doc, "auto_transition"), + baseRef: nullableString(doc, "base_ref"), + designDoc: relativePath(doc, "design_doc"), + plan: relativePath(doc, "plan"), + verifyResult: enumValue(doc, "verify_result", VERIFY_RESULTS, false), + verifyFailures: nonNegativeInteger(doc, "verify_failures"), + verificationReport: relativePath(doc, "verification_report"), + branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES), + createdAt: nullableString(doc, "created_at"), + verifiedAt: nullableString(doc, "verified_at"), + archiveConfirmation: enumValue(doc, "archive_confirmation", ARCHIVE_CONFIRMATIONS), + archived: booleanValue(doc, "archived", false), + directOverride: booleanValue(doc, "direct_override"), + handoffContext: relativePath(doc, "handoff_context"), + handoffHash: sha256(doc, "handoff_hash"), + classicProfile: enumValue(doc, "classic_profile", CLASSIC_PROFILES), + classicMigration: migrationVersion(doc) + }; +} +function parseClassicStateDocument(doc, run) { + let resolvedRun = run ?? null; + if (resolvedRun === null && run === void 0) { + if (doc.run_id && doc.skill) { + try { + resolvedRun = runStateFromDocument(doc); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(message.replace(/^Invalid Run state:/u, "Invalid Classic state:"), { + cause: error + }); + } + } + } + return { + classic: classicStateFromDocument(doc), + run: resolvedRun, + unknownKeys: Object.keys(doc).filter((key) => !KNOWN_KEYS.has(key)) + }; +} +function readLegacyStateSummary(doc) { + const workflowRaw = doc["workflow"]; + const phaseRaw = doc["phase"]; + const archivedRaw = doc["archived"]; + const designDocRaw = doc["design_doc"]; + return { + workflow: typeof workflowRaw === "string" && CLASSIC_PROFILES.includes(workflowRaw) ? workflowRaw : null, + phase: typeof phaseRaw === "string" && PHASES.includes(phaseRaw) ? phaseRaw : null, + archived: archivedRaw === true, + designDoc: typeof designDocRaw === "string" && designDocRaw !== "" ? designDocRaw : null, + unknownKeys: Object.keys(doc).filter((key) => !KNOWN_KEYS.has(key)) + }; +} + +// domains/comet-classic/classic-store.ts +init_state(); +function documentRecord(document) { + const value = document.toJS(); + if (value === null) return {}; + if (typeof value !== "object" || Array.isArray(value)) { + throw new Error("Invalid Classic state document: root must be a mapping"); + } + return value; +} +function stripLegacyRunFields(document) { + const LEGACY_RUN_KEYS = [ + "skill", + "skill_version", + "skill_hash", + "orchestration", + "current_step", + "iteration", + "pending", + "pending_ref", + "trajectory_ref", + "context_ref", + "artifacts_ref", + "checkpoint_ref", + "run_status", + "run_retries" + ]; + for (const key of LEGACY_RUN_KEYS) document.delete(key); +} +function stripLegacyCommandFields(document) { + let changed = false; + for (const key of ["build_command", "verify_command"]) { + if (document.has(key)) { + document.delete(key); + changed = true; + } + } + return changed; +} +async function readDocument(file) { + let source; + try { + source = await fs5.readFile(file, "utf8"); + } catch (error) { + if (error.code !== "ENOENT") throw error; + return new import_yaml2.Document({}); + } + const document = (0, import_yaml2.parseDocument)(source); + if (document.errors.length > 0) { + throw new Error(`Invalid Classic state document: ${document.errors[0].message}`); + } + documentRecord(document); + return document; +} +async function readClassicState(changeDir, options = {}) { + const shouldMigrate = options.migrate !== false; + const file = path5.join(changeDir, ".comet.yaml"); + const document = await readDocument(file); + let doc = documentRecord(document); + let migrated = stripLegacyCommandFields(document); + if (migrated) doc = documentRecord(document); + let run = await readRunState(changeDir); + if (!run && doc.run_id && doc.skill) { + const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports)); + run = runStateFromDocument2(doc); + if (run && shouldMigrate) { + await writeRunState(changeDir, run); + stripLegacyRunFields(document); + migrated = true; + } + } + if (migrated && shouldMigrate) { + const temporary = path5.join(changeDir, `.comet.yaml.${randomUUID3()}.tmp`); + await fs5.writeFile(temporary, document.toString(), "utf8"); + await fs5.rename(temporary, file); + } + return parseClassicStateDocument(documentRecord(document), run); +} +async function readLegacyState(changeDir) { + const document = await readDocument(path5.join(changeDir, ".comet.yaml")); + return readLegacyStateSummary(documentRecord(document)); +} + +// domains/comet-classic/classic-current-change.ts +function changeDirectory(projectRoot, changeName) { + return path6.join(projectRoot, "openspec", "changes", changeName); +} +async function validateActiveChange(projectRoot, changeName) { + assertOpenSpecChangeName(changeName); + const changeDir = changeDirectory(projectRoot, changeName); + try { + await fs6.access(path6.join(changeDir, ".comet.yaml")); + } catch (error) { + if (error.code === "ENOENT") { + throw new Error( + `Cannot select current change '${changeName}': active change state not found`, + { + cause: error + } + ); + } + throw error; + } + const projection = await readClassicState(changeDir, { migrate: false }); + if (!projection.classic) { + throw new Error(`Cannot select current change '${changeName}': Classic state is incomplete`); + } + if (projection.classic.archived) { + throw new Error(`Cannot select current change '${changeName}': change is archived`); + } +} +async function resolveCurrentChange(projectRoot) { + let current; + try { + current = await readCometCurrentSelection(projectRoot); + } catch (error) { + return { + status: "stale", + reason: error instanceof Error ? error.message : String(error) + }; + } + if (current.status === "missing") return { status: "missing" }; + if (current.selection.workflow !== "classic") { + return { + status: "stale", + reason: `current change '${current.selection.change}' belongs to Native, not Classic` + }; + } + const selection = current.selection; + try { + await validateActiveChange(projectRoot, selection.change); + } catch (error) { + return { + status: "stale", + reason: error instanceof Error ? error.message : String(error) + }; + } + const outcome = await resolveBranchBinding(changeDirectory(projectRoot, selection.change), { + heal: false, + cwd: projectRoot + }); + if (outcome.status === "drift") { + return { + status: "stale", + reason: driftStaleReason(selection.change, outcome.boundBranch, outcome.currentBranch) + }; + } + if (outcome.status === "unbound-detached") { + return { status: "stale", reason: unboundDetachedMessage(selection.change) }; + } + if (outcome.status === "ok") return { status: "selected", selection }; + if (selection.branch !== null && outcome.currentBranch !== selection.branch) { + return { + status: "stale", + reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${outcome.currentBranch ?? "detached HEAD"}'` + }; + } + return { status: "selected", selection }; +} + +// domains/comet-classic/classic-hook-guard.ts +function result(exitCode, message) { + return { exitCode, stderr: message + "\n" }; +} +function allowed(message) { + return result(0, `[COMET-HOOK] allowed: ${message}`); +} +function normalized(value) { + return value.replaceAll("\\", "/").replace(/\/+/gu, "/"); +} +function comparisonKey(value) { + const normalizedValue = normalized(value); + return process.platform === "win32" ? normalizedValue.toLowerCase() : normalizedValue; +} +function relativeToProjectRoot(target, projectRoot) { + const relative = normalized(path7.relative(projectRoot, target)); + if (relative === "") return ""; + if (relative.startsWith("../") || relative === ".." || path7.isAbsolute(relative)) return null; + return relative; +} +async function physicalPathForPossiblyMissingTarget(target) { + const resolved = path7.resolve(target); + const root = path7.parse(resolved).root; + const missingSegments = []; + let cursor = resolved; + while (cursor && cursor !== root) { + try { + const physicalBase = await fs7.realpath(cursor); + return path7.join(physicalBase, ...missingSegments.reverse()); + } catch (error) { + const code = error.code; + if (code !== "ENOENT" && code !== "ENOTDIR") throw error; + missingSegments.push(path7.basename(cursor)); + cursor = path7.dirname(cursor); + } + } + try { + const physicalRoot = await fs7.realpath(root); + return path7.join(physicalRoot, ...missingSegments.reverse()); + } catch { + return null; + } +} +async function projectRelative(target, projectRoot) { + const rawCandidate = path7.isAbsolute(target) ? target : path7.resolve(process.cwd(), target); + let candidate = normalized(rawCandidate); + const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot); + if (rootRelative !== null) return rootRelative; + try { + const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate); + const physicalRoot = await fs7.realpath(projectRoot); + if (physicalCandidate) { + const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot); + if (physicalRootRelative !== null) return physicalRootRelative; + candidate = normalized(physicalCandidate); + } + } catch { + if (!path7.isAbsolute(target)) return normalized(target).replace(/^\.\//u, ""); + } + return candidate.replace(/^\.\//u, ""); +} +async function loadGoverningChange(changeDir) { + try { + const projection = await readClassicState(changeDir, { migrate: false }); + const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort(); + if (unknownKeys.length > 0) { + throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`); + } + if (!projection.classic) throw new Error("Classic state projection is unavailable"); + return { + changeDir, + phase: projection.classic.phase, + classic: projection.classic, + archived: projection.classic.archived + }; + } catch { + const legacy = await readLegacyState(changeDir); + if (!legacy.phase) return null; + return { + changeDir, + phase: legacy.phase, + classic: null, + archived: legacy.archived + }; + } +} +async function activeChanges(projectRoot) { + const changesDir = path7.join(projectRoot, "openspec", "changes"); + const governingChanges = []; + if (!existsSync(changesDir)) return governingChanges; + for (const entry of (await fs7.readdir(changesDir, { withFileTypes: true })).sort( + (left, right) => left.name.localeCompare(right.name) + )) { + if (!entry.isDirectory() || entry.name === "archive") continue; + const changeDir = path7.join(changesDir, entry.name); + if (!existsSync(path7.join(changeDir, ".comet.yaml"))) continue; + const governing = await loadGoverningChange(changeDir); + if (!governing || governing.archived) continue; + governingChanges.push(governing); + } + return governingChanges; +} +async function listActiveClassicHookChanges(projectRoot) { + return (await activeChanges(projectRoot)).map((change) => ({ + workflow: "classic", + name: governingChangeName(change), + phase: change.phase + })); +} +function isSuperpowersArtifactPath(relativePath2) { + return comparisonKey(relativePath2).startsWith("docs/superpowers/"); +} +var SUPERPOWERS_ARTIFACT_SLOTS = [ + { + prefix: "docs/superpowers/specs/", + field: "designDoc", + wireField: "design_doc", + phase: "design" + }, + { + prefix: "docs/superpowers/plans/", + field: "plan", + wireField: "plan", + phase: "build" + }, + { + prefix: "docs/superpowers/reports/", + field: "verificationReport", + wireField: "verification_report", + phase: "verify" + } +]; +function standardSuperpowersArtifactSlot(relativePath2) { + const key = comparisonKey(relativePath2); + const slot = SUPERPOWERS_ARTIFACT_SLOTS.find((candidate) => key.startsWith(candidate.prefix)); + if (!slot) return null; + const fileName = key.slice(slot.prefix.length); + if (!fileName || fileName.includes("/") || !fileName.endsWith(".md")) return null; + return slot; +} +function superpowersArtifactValue(governing, slot) { + return governing.classic?.[slot.field] ?? null; +} +function allowsFirstSuperpowersArtifactWrite(governing, slot) { + return governing.classic !== null && governing.phase === slot.phase && !superpowersArtifactValue(governing, slot); +} +function allowsSuperpowersArtifacts(governing) { + return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify"; +} +function governingChangeName(governing) { + return governing.changeDir ? path7.basename(governing.changeDir) : null; +} +var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([ + "design", + "plan", + "verify", + "verification", + "verification-report", + "report" +]); +function escapeRegex(value) { + return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"); +} +function matchesRecordedSuperpowersArtifact(relativePath2, governing) { + const artifactPaths = [ + governing.classic?.designDoc, + governing.classic?.plan, + governing.classic?.verificationReport + ]; + return artifactPaths.some( + (artifactPath) => artifactPath && comparisonKey(artifactPath) === comparisonKey(relativePath2) + ); +} +function matchesSuperpowersArtifactName(relativePath2, changeName) { + const fileName = relativePath2.split("/").at(-1) ?? relativePath2; + const stem = fileName.replace(/\.[^.]+$/u, ""); + if (stem === changeName) return true; + const suffixes = [...SUPERPOWERS_ARTIFACT_SUFFIXES].map(escapeRegex).join("|"); + const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u"); + return pattern.test(stem); +} +async function superpowersArtifactGoverningChange(relativePath2, projectRoot) { + const active = await activeChanges(projectRoot); + const recorded = active.find( + (governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing) + ); + if (recorded) return { governing: recorded, match: "recorded" }; + const eligible = active.filter(allowsSuperpowersArtifacts); + const named = eligible.filter((governing) => { + const name = governingChangeName(governing); + return name !== null && matchesSuperpowersArtifactName(relativePath2, name); + }).sort( + (a, b) => (governingChangeName(b)?.length ?? 0) - (governingChangeName(a)?.length ?? 0) + )[0]; + if (named) return { governing: named, match: "named" }; + return null; +} +async function repoSourceGoverningChange(projectRoot, relativePath2, selectedChangeName) { + const active = await activeChanges(projectRoot); + if (active.length === 0) return null; + if (selectedChangeName) { + const selected = active.find( + (governing) => governingChangeName(governing) === selectedChangeName + ); + return selected ?? { + blockedResult: blockedStaleSelection( + relativePath2, + `selected change '${selectedChangeName}' is no longer active` + ) + }; + } + const current = await resolveCurrentChange(projectRoot); + if (current.status === "stale") { + return { blockedResult: blockedStaleSelection(relativePath2, current.reason) }; + } + if (current.status === "selected") { + const selected = active.find( + (governing) => governingChangeName(governing) === current.selection.change + ); + if (selected) return selected; + return { + blockedResult: blockedStaleSelection( + relativePath2, + `selected change '${current.selection.change}' is no longer active` + ) + }; + } + if (active.length === 1) { + const sole = active[0]; + if (sole.changeDir !== null) { + const outcome = await resolveBranchBinding(sole.changeDir, { + heal: false, + cwd: projectRoot + }); + const name = governingChangeName(sole) ?? "unknown"; + if (outcome.status === "drift") { + return { + blockedResult: blockedStaleSelection( + relativePath2, + driftStaleReason(name, outcome.boundBranch, outcome.currentBranch) + ) + }; + } + if (outcome.status === "unbound-detached") { + return { + blockedResult: blockedStaleSelection(relativePath2, unboundDetachedMessage(name)) + }; + } + } + return sole; + } + return { + blockedResult: blockedMultipleChanges( + relativePath2, + active.map((governing) => governingChangeName(governing)).filter(Boolean) + ) + }; +} +async function governingChange(relativePath2, projectRoot, selectedChangeName) { + const prefix = "openspec/changes/"; + if (relativePath2.startsWith(prefix)) { + const rest = relativePath2.slice(prefix.length); + const [name] = rest.split("/"); + if (name && name !== "archive") { + const changeDir = path7.join(projectRoot, "openspec", "changes", name); + const stateFile = path7.join(changeDir, ".comet.yaml"); + if (existsSync(stateFile)) { + const governing = await loadGoverningChange(changeDir); + if (governing) return governing; + return { changeDir, phase: "open", classic: null, archived: false }; + } + return { changeDir, phase: "open", classic: null, archived: false }; + } + } + if (isSuperpowersArtifactPath(relativePath2)) { + const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot); + if (superpowers?.match === "recorded") { + return { ...superpowers.governing, superpowersArtifact: "matched" }; + } + const slot = standardSuperpowersArtifactSlot(relativePath2); + if (superpowers) { + return slot ? { + ...superpowers.governing, + superpowersArtifact: allowsFirstSuperpowersArtifactWrite(superpowers.governing, slot) ? "matched" : "unmatched", + superpowersSlot: slot + } : { ...superpowers.governing, superpowersArtifact: "matched" }; + } + if (slot) { + const candidate = await repoSourceGoverningChange( + projectRoot, + relativePath2, + selectedChangeName + ); + if (!candidate || "blockedResult" in candidate) return candidate; + return { + ...candidate, + superpowersArtifact: allowsFirstSuperpowersArtifactWrite(candidate, slot) ? "matched" : "unmatched", + superpowersSlot: slot + }; + } + const active = await activeChanges(projectRoot); + const fallback = selectedChangeName ? active.find((candidate) => governingChangeName(candidate) === selectedChangeName) ?? null : active[0] ?? null; + return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null; + } + return repoSourceGoverningChange(projectRoot, relativePath2, selectedChangeName); +} +function isRootMarkdown(relativePath2) { + return !relativePath2.includes("/") && relativePath2.endsWith(".md"); +} +function isCometConfig(relativePath2) { + return relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/"); +} +function isSuperpowersWorkspace(relativePath2) { + return relativePath2 === ".superpowers" || relativePath2.startsWith(".superpowers/"); +} +function openSpecAllowed(relativePath2, phase) { + if (!relativePath2.startsWith("openspec/")) return null; + const stateFile = relativePath2.endsWith("/.comet.yaml") || relativePath2.endsWith("/.openspec.yaml"); + const proposal = relativePath2.endsWith("/proposal.md") || relativePath2.endsWith("/design.md") || relativePath2.endsWith("/tasks.md"); + const handoff = relativePath2.includes("/.comet/"); + const specs = relativePath2.includes("/specs/"); + if (phase === "open" && (proposal || stateFile || handoff || specs)) { + return `${relativePath2} (phase: open, openspec artifacts)`; + } + if (phase === "design" && (proposal || stateFile || handoff || specs)) { + return `${relativePath2} (phase: design, handoff/spec)`; + } + if (phase === "build" && (relativePath2.endsWith("/tasks.md") || stateFile || specs)) { + return `${relativePath2} (phase: build, spec/tasks)`; + } + if (phase === "verify" && (relativePath2.endsWith("/tasks.md") || stateFile)) { + return `${relativePath2} (phase: verify, tasks/state)`; + } + if (phase === "archive" && stateFile) { + return `${relativePath2} (phase: archive, state)`; + } + return null; +} +function blocked(relativePath2, phase) { + const guidance = phase === "open" ? [ + " BLOCKED: source writes are not allowed during open", + " This phase does not allow source writes", + " ALLOWED: create proposal/design/tasks artifacts and run guard", + " NEXT: finish clarification and artifacts, then run guard --apply" + ] : phase === "design" ? [ + " BLOCKED: source writes are not allowed during design", + " This phase does not allow source writes", + " ALLOWED: run brainstorming, create the Design Doc, and run guard", + " NEXT: finish the Design Doc, then run comet guard design --apply to enter build" + ] : [ + " BLOCKED: source writes are not allowed during archive", + " This phase does not allow source writes", + " ALLOWED: confirm archive state and run the archive script" + ]; + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + ` Current phase: ${phase}`, + ` Target file: ${relativePath2}`, + "", + ...guidance, + "" + ].join("\n") + ); +} +function blockedMissingDesignDoc(relativePath2) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " Current phase: build (workflow: full), but design_doc is empty", + ` Target file: ${relativePath2}`, + "", + " BLOCKED: full workflow source writes require a recorded Design Doc", + " This phase does not allow source writes until design_doc is recorded", + " NEXT: return to design, create/link the Design Doc, then run guard again", + "" + ].join("\n") + ); +} +function blockedUnmatchedSuperpowersArtifact(relativePath2, governing) { + const slot = governing.superpowersSlot; + const recorded = slot ? superpowersArtifactValue(governing, slot) : null; + const details = slot ? governing.phase !== slot.phase ? [ + ` BLOCKED: ${slot.wireField} cannot be first-written in phase ${governing.phase}`, + ` Expected phase: ${slot.phase}`, + " NEXT: resume the matching Comet phase or use an already recorded artifact path" + ] : recorded ? [ + ` BLOCKED: ${slot.wireField} is already recorded for this change`, + ` Recorded path: ${recorded}`, + " NEXT: write the recorded artifact or explicitly correct the state path" + ] : [ + " BLOCKED: standard Superpowers artifact state is incomplete", + " NEXT: validate the active change state, then retry the matching phase" + ] : [ + " BLOCKED: unmatched Superpowers artifact", + " This docs/superpowers/ path does not match any active change artifact", + " NEXT: use a recorded artifact path or a standard phase artifact directory" + ]; + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + ` Current phase: ${governing.phase}`, + ` Target file: ${relativePath2}`, + "", + ...details, + "" + ].join("\n") + ); +} +function blockedMultipleChanges(relativePath2, changeNames) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " BLOCKED: multiple active changes require a current change", + ` Target file: ${relativePath2}`, + ` Active changes: ${changeNames.join(", ")}`, + "", + " NEXT: run comet state select , then retry the source write", + "" + ].join("\n") + ); +} +function blockedStaleSelection(relativePath2, reason) { + return result( + 2, + [ + "", + "╔══════════════════════════════════════════╗", + "║ COMET PHASE GUARD — WRITE BLOCKED ║", + "╚══════════════════════════════════════════╝", + "", + " BLOCKED: current change selection is stale or invalid", + ` Target file: ${relativePath2}`, + ` Reason: ${reason}`, + "", + " NEXT: run comet state select , then retry the source write", + "" + ].join("\n") + ); +} +async function inspectClassicHookTarget(projectRoot, target, selectedChangeName) { + const relativePath2 = await projectRelative(target, projectRoot); + if (isCometConfig(relativePath2)) { + return allowed(`${relativePath2} (whitelist: comet config)`); + } + if (relativePath2.startsWith(".claude/")) { + return allowed(`${relativePath2} (whitelist: claude config)`); + } + if (isSuperpowersWorkspace(relativePath2)) { + return allowed(`${relativePath2} (whitelist: superpowers workspace)`); + } + if (relativePath2 === "CLAUDE.md" || relativePath2 === "CHANGELOG.md" || relativePath2 === "README.md" || isRootMarkdown(relativePath2)) { + return allowed(`${relativePath2} (whitelist: root markdown)`); + } + let governing; + try { + governing = await governingChange(relativePath2, projectRoot, selectedChangeName); + } catch (error) { + return result( + 2, + `[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}` + ); + } + if (!governing) return allowed("no active comet change"); + if ("blockedResult" in governing) return governing.blockedResult; + if (governing.archived) return allowed(`${relativePath2} (own change archived)`); + const phase = governing.phase; + const openSpec = openSpecAllowed(relativePath2, phase); + if (openSpec) return allowed(openSpec); + if (isSuperpowersArtifactPath(relativePath2)) { + if (governing.superpowersArtifact === "matched" && allowsSuperpowersArtifacts(governing)) { + return allowed(`${relativePath2} (phase: ${phase}, superpowers)`); + } + if (governing.superpowersArtifact === "unmatched") { + return blockedUnmatchedSuperpowersArtifact(relativePath2, governing); + } + } + if (phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc) { + return blockedMissingDesignDoc(relativePath2); + } + if (phase === "build" || phase === "verify") { + return allowed(`${relativePath2} (phase: ${phase})`); + } + return blocked(relativePath2, phase); +} +async function inspectClassicHookGuard(projectRoot, changeName, request) { + const active = await activeChanges(projectRoot); + const selected = active.find((change) => governingChangeName(change) === changeName); + if (!selected) { + return { + allowed: false, + reason: `Selected Classic change ${changeName} is missing or archived; resume /comet-classic before retrying`, + workflow: "classic", + change: changeName + }; + } + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + if (selected.phase === "verify" || selected.phase === "build" && !(selected.classic?.workflow === "full" && !selected.classic.designDoc)) { + return { + allowed: true, + reason: `Classic change is in ${selected.phase}`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + return { + allowed: false, + reason: `Hook write target could not be determined while Classic change ${changeName} is in ${selected.phase}; resume /comet-classic before retrying`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + for (const target of request.targets) { + const inspected = await inspectClassicHookTarget(projectRoot, target, changeName); + if (inspected.exitCode !== 0) { + return { + allowed: false, + reason: inspected.stderr?.trim() || "Classic phase guard blocked the write", + workflow: "classic", + change: changeName, + phase: selected.phase + }; + } + } + return { + allowed: true, + reason: `Classic write allowed in ${selected.phase}`, + workflow: "classic", + change: changeName, + phase: selected.phase + }; +} + +// domains/comet-native/native-hook-guard.ts +import { promises as fs12 } from "fs"; +import path12 from "path"; + +// domains/comet-native/native-change.ts +var import_yaml5 = __toESM(require_dist(), 1); +import { promises as fs11 } from "fs"; +import path11 from "path"; + +// domains/comet-native/native-bounded-file.ts +import { createHash } from "node:crypto"; +import { promises as fs8 } from "node:fs"; +import path8 from "node:path"; +import { TextDecoder } from "node:util"; + +// domains/comet-native/native-sensitive-paths.ts +var NATIVE_EXCLUDED_DIRECTORY_NAMES = /* @__PURE__ */ new Set([ + ".cache", + ".git", + ".gradle", + ".gnupg", + ".mypy_cache", + ".next", + ".npm", + ".pnpm-store", + ".pytest_cache", + ".ssh", + ".turbo", + ".venv", + ".yarn", + "__pycache__", + "node_modules", + "venv" +]); +var NATIVE_SENSITIVE_FILE_NAMES = /* @__PURE__ */ new Set([ + ".git-credentials", + ".netrc", + ".npmrc", + ".pypirc", + "auth.json", + "credentials.json" +]); +function isNativeEnvFileName(name) { + return name.toLowerCase().startsWith(".env"); +} +function nativeSensitiveRelativePathReason(relativeRef) { + const segments = relativeRef.replaceAll("\\", "/").split("/").filter(Boolean); + const lower = segments.map((segment) => segment.toLowerCase()); + if (lower.some((segment) => isNativeEnvFileName(segment))) return "environment-file"; + if (lower.some((segment) => NATIVE_SENSITIVE_FILE_NAMES.has(segment))) { + return "credential-config"; + } + if (lower.includes(".git")) return "git-metadata"; + if (lower.some((segment) => NATIVE_EXCLUDED_DIRECTORY_NAMES.has(segment))) { + return "dependency-or-cache"; + } + if (lower.join("/") === ".comet/config.yaml") { + return "comet-config"; + } + if (lower.join("/") === ".comet/current-change.json") { + return "comet-selection"; + } + return null; +} + +// domains/comet-native/native-bounded-file.ts +var DEFAULT_NATIVE_ARTIFACT_MAX_BYTES = 1024 * 1024; +function isInside(parent, target) { + const relative = path8.relative(parent, target); + return relative === "" || !path8.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path8.sep}`); +} +function portableArtifactRef(value) { + const normalized2 = path8.posix.normalize(value); + if (value.length === 0 || value !== value.trim() || value.includes("\\") || Array.from(value).some((character) => { + const code = character.codePointAt(0) ?? 0; + return code <= 31 || code === 127; + }) || path8.posix.isAbsolute(normalized2) || /^(?:[A-Za-z]:|~)/u.test(value) || value.split("/").includes("..") || normalized2 !== value || normalized2 === "." || value.endsWith("/")) { + throw new Error(`Native artifact ref must be normalized and relative: ${value}`); + } + const lower = value.toLowerCase(); + const sensitiveReason = nativeSensitiveRelativePathReason(value); + if (sensitiveReason || lower === "runtime" || lower.startsWith("runtime/")) { + throw new Error( + `Native artifact ref is excluded as sensitive (${sensitiveReason ?? "native-runtime"}): ${value}` + ); + } + return value; +} +function positiveLimit(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native artifact byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity(identity, stat) { + return sameFileObject( + { ...identity, birthtime: identity.birthtimeMs }, + { + ...stat, + birthtime: stat.birthtimeMs + } + ); +} +function sameFileIdentity(left, right) { + const leftObject = { ...left, birthtime: left.birthtimeMs }; + const rightObject = { ...right, birthtime: right.birthtimeMs }; + if (hasComparableFileObject(leftObject, rightObject)) { + return sameFileObject(leftObject, rightObject); + } + return sameFileObject(leftObject, rightObject) && left.birthtimeMs === right.birthtimeMs && left.ctimeMs === right.ctimeMs && left.size === right.size; +} +async function directoryIdentity(directory) { + const stat = await fs8.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`Native artifact parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs8.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain(root, directory) { + const lexicalRoot = path8.resolve(root); + const lexicalDirectory = path8.resolve(directory); + if (!isInside(lexicalRoot, lexicalDirectory)) { + throw new Error("Native artifact path is outside its root"); + } + const chain = [await directoryIdentity(lexicalRoot)]; + let cursor = lexicalRoot; + for (const segment of path8.relative(lexicalRoot, lexicalDirectory).split(path8.sep).filter(Boolean)) { + cursor = path8.join(cursor, segment); + const identity = await directoryIdentity(cursor); + if (!isInside(chain[0].realPath, identity.realPath)) { + throw new Error(`Native artifact parent resolves outside its root: ${cursor}`); + } + chain.push(identity); + } + return chain; +} +async function verifyDirectoryChain(chain) { + for (const identity of chain) { + const stat = await fs8.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity(identity, stat) || await fs8.realpath(identity.path) !== identity.realPath) { + throw new Error(`Native artifact parent changed while reading: ${identity.path}`); + } + } +} +async function readNativeBoundedTextFile(options) { + const ref = portableArtifactRef(options.ref); + const maxBytes = positiveLimit(options.maxBytes ?? DEFAULT_NATIVE_ARTIFACT_MAX_BYTES); + const file = path8.resolve(options.root, ...ref.split("/")); + const chain = await captureDirectoryChain(options.root, path8.dirname(file)); + await options.hooks?.afterParentChainCaptured?.(); + const before = await fs8.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`Native artifact must be a regular file: ${ref}`); + } + if (before.size > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + const realPath = await fs8.realpath(file); + if (!isInside(chain[0].realPath, realPath)) { + throw new Error(`Native artifact resolves outside its root: ${ref}`); + } + const handle = await fs8.open(file, "r"); + try { + const [opened, afterOpenPath, afterOpenRealPath] = await Promise.all([ + handle.stat(), + fs8.lstat(file), + fs8.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!opened.isFile() || !afterOpenPath.isFile() || afterOpenPath.isSymbolicLink() || afterOpenRealPath !== realPath || !sameFileIdentity(before, opened) || !sameFileIdentity(opened, afterOpenPath)) { + throw new Error(`Native artifact changed while opening: ${ref}`); + } + await options.hooks?.afterOpen?.(); + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const result2 = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (result2.bytesRead === 0) break; + total += result2.bytesRead; + if (total > maxBytes) throw new Error(`Native artifact exceeds ${maxBytes} bytes: ${ref}`); + chunks.push(Buffer.from(buffer.subarray(0, result2.bytesRead))); + } + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs8.lstat(file), + fs8.realpath(file) + ]); + await verifyDirectoryChain(chain); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== realPath || !sameFileIdentity(opened, afterHandle) || !sameFileIdentity(opened, afterPath)) { + throw new Error(`Native artifact changed while reading: ${ref}`); + } + const bytes = Buffer.concat(chunks, total); + let text; + try { + text = new TextDecoder("utf-8", { fatal: true }).decode(bytes); + } catch (error) { + throw new Error(`Native artifact is not valid UTF-8: ${ref}`, { cause: error }); + } + return { + ref, + size: total, + hash: createHash("sha256").update(bytes).digest("hex"), + text + }; + } finally { + await handle.close(); + } +} + +// domains/comet-native/native-config.ts +var import_yaml4 = __toESM(require_dist(), 1); +import { promises as fs10 } from "fs"; +import path10 from "path"; + +// domains/workflow-contract/project-config.ts +var import_yaml3 = __toESM(require_dist(), 1); + +// domains/comet-native/native-protected-file.ts +import { createHash as createHash2 } from "node:crypto"; +import { constants as fsConstants2, promises as fs9 } from "node:fs"; +import path9 from "node:path"; +import { TextDecoder as TextDecoder2 } from "node:util"; +function isInside2(parent, target) { + const relative = path9.relative(parent, target); + return relative === "" || !path9.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path9.sep}`); +} +function positiveLimit2(value) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error("Native protected file byte limit must be a positive integer"); + } + return value; +} +function sameDirectoryIdentity2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ); +} +function asFileIdentity(stat) { + return { + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs, + ctimeMs: stat.ctimeMs, + mtimeMs: stat.mtimeMs, + size: stat.size + }; +} +function sameFileIdentity2(expected, actual) { + return sameFileObject( + { ...expected, birthtime: expected.birthtimeMs }, + { + ...actual, + birthtime: actual.birthtimeMs + } + ) && expected.birthtimeMs === actual.birthtimeMs && expected.ctimeMs === actual.ctimeMs && expected.mtimeMs === actual.mtimeMs && expected.size === actual.size; +} +async function captureDirectoryIdentity(directory, label) { + const stat = await fs9.lstat(directory); + if (!stat.isDirectory() || stat.isSymbolicLink()) { + throw new Error(`${label} parent must be a real directory: ${directory}`); + } + return { + path: directory, + realPath: await fs9.realpath(directory), + dev: stat.dev, + ino: stat.ino, + birthtimeMs: stat.birthtimeMs + }; +} +async function captureDirectoryChain2(root, directory, label) { + const lexicalRoot = path9.resolve(root); + const lexicalDirectory = path9.resolve(directory); + if (!isInside2(lexicalRoot, lexicalDirectory)) { + throw new Error(`${label} is outside its managed root`); + } + const chain = [await captureDirectoryIdentity(lexicalRoot, label)]; + let cursor = lexicalRoot; + for (const segment of path9.relative(lexicalRoot, lexicalDirectory).split(path9.sep).filter(Boolean)) { + await verifyDirectoryChain2(chain, label); + cursor = path9.join(cursor, segment); + const identity = await captureDirectoryIdentity(cursor, label); + if (!isInside2(chain[0].realPath, identity.realPath)) { + throw new Error(`${label} parent resolves outside its managed root: ${cursor}`); + } + chain.push(identity); + } + await verifyDirectoryChain2(chain, label); + return chain; +} +async function verifyDirectoryChain2(chain, label) { + for (const identity of chain) { + const stat = await fs9.lstat(identity.path); + if (!stat.isDirectory() || stat.isSymbolicLink() || !sameDirectoryIdentity2(identity, stat) || await fs9.realpath(identity.path) !== identity.realPath) { + throw new Error(`${label} parent changed during I/O: ${identity.path}`); + } + } +} +async function readHandleBounded(handle, maxBytes, label) { + const chunks = []; + let total = 0; + const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maxBytes + 1)); + while (true) { + const remaining = maxBytes + 1 - total; + const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, remaining), null); + if (bytesRead === 0) break; + total += bytesRead; + if (total > maxBytes) throw new Error(`${label} exceeds ${maxBytes} bytes`); + chunks.push(Buffer.from(buffer.subarray(0, bytesRead))); + } + return Buffer.concat(chunks, total); +} +async function readNativeProtectedFile(options) { + const maxBytes = positiveLimit2(options.maxBytes); + const file = path9.resolve(options.file); + const chain = await captureDirectoryChain2(options.root, path9.dirname(file), options.label); + const forbidden = await Promise.all( + (options.forbiddenRoots ?? []).map( + (root) => captureDirectoryIdentity(path9.resolve(root), options.label) + ) + ); + await options.hooks?.afterParentChainCaptured?.(); + await verifyDirectoryChain2(chain, options.label); + const before = await fs9.lstat(file); + if (!before.isFile() || before.isSymbolicLink()) { + throw new Error(`${options.label} must be a regular file`); + } + if (before.size > maxBytes) throw new Error(`${options.label} exceeds ${maxBytes} bytes`); + const beforeIdentity = asFileIdentity(before); + const beforeRealPath = await fs9.realpath(file); + if (!isInside2(chain[0].realPath, beforeRealPath)) { + throw new Error(`${options.label} resolves outside its managed root`); + } + if (forbidden.some((identity) => isInside2(identity.realPath, beforeRealPath))) { + throw new Error(`${options.label} resolves inside an excluded root`); + } + const flags = process.platform === "win32" ? fsConstants2.O_RDONLY : fsConstants2.O_RDONLY | fsConstants2.O_NOFOLLOW | fsConstants2.O_NONBLOCK; + const handle = await fs9.open(file, flags); + try { + const opened = await handle.stat(); + await options.hooks?.afterOpen?.(); + const [pathAfterOpen, realPathAfterOpen] = await Promise.all([ + fs9.lstat(file), + fs9.realpath(file) + ]); + await verifyDirectoryChain2(chain, options.label); + await verifyDirectoryChain2(forbidden, options.label); + if (!opened.isFile() || !pathAfterOpen.isFile() || pathAfterOpen.isSymbolicLink() || realPathAfterOpen !== beforeRealPath || !sameFileIdentity2(beforeIdentity, opened) || !sameFileIdentity2(beforeIdentity, pathAfterOpen)) { + throw new Error(`${options.label} changed while opening`); + } + await options.hooks?.beforeRead?.(); + const bytes = await readHandleBounded(handle, maxBytes, options.label); + await options.hooks?.beforeFinalCheck?.(); + const [afterHandle, afterPath, afterRealPath] = await Promise.all([ + handle.stat(), + fs9.lstat(file), + fs9.realpath(file) + ]); + await verifyDirectoryChain2(chain, options.label); + await verifyDirectoryChain2(forbidden, options.label); + if (!afterPath.isFile() || afterPath.isSymbolicLink() || afterRealPath !== beforeRealPath || !sameFileIdentity2(beforeIdentity, afterHandle) || !sameFileIdentity2(beforeIdentity, afterPath)) { + throw new Error(`${options.label} changed while reading`); + } + return { + bytes, + hash: createHash2("sha256").update(bytes).digest("hex"), + size: bytes.length + }; + } finally { + await handle.close(); + } +} +async function readNativeProtectedTextFile(options) { + const snapshot = await readNativeProtectedFile(options); + let text; + try { + text = new TextDecoder2("utf-8", { fatal: true }).decode(snapshot.bytes); + } catch (error) { + throw new Error(`${options.label} is not valid UTF-8`, { cause: error }); + } + return { ...snapshot, text }; +} + +// domains/comet-native/native-config.ts +var NATIVE_KEYS = /* @__PURE__ */ new Set([ + "artifact_root", + "language", + "clarification_mode", + "snapshot", + "pending_root_move" +]); +var SNAPSHOT_KEYS = /* @__PURE__ */ new Set([ + "include", + "exclude", + "max_files", + "max_total_bytes", + "max_duration_ms" +]); +var PENDING_KEYS = /* @__PURE__ */ new Set(["id", "from_artifact_root", "to_artifact_root", "stage", "cleanup"]); +var NATIVE_PROJECT_CONFIG_MAX_BYTES = 64 * 1024; +var CLEANUP_KEYS = /* @__PURE__ */ new Set(["kind", "state", "manifest_hash"]); +var MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH = 1024; +var MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS = 64; +var DEFAULT_NATIVE_SNAPSHOT_CONFIG = { + include: ["**/*"], + exclude: [], + max_files: 1e4, + max_total_bytes: 256 * 1024 * 1024, + max_duration_ms: 6e4 +}; +function normalizeNativeSnapshotPattern(value, label) { + if (typeof value !== "string" || value.length === 0 || value.includes("\\") || value.includes("\0") || value.startsWith("/") || value.split("/").includes("..")) { + throw new Error(`${label} contains an unsafe pattern`); + } + if (value.length > MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH) { + throw new Error(`${label} exceeds ${MAX_NATIVE_SNAPSHOT_PATTERN_LENGTH} characters`); + } + let wildcardTokens = 0; + for (let index = 0; index < value.length; index += 1) { + if (value[index] === "?") { + wildcardTokens += 1; + } else if (value[index] === "*") { + wildcardTokens += 1; + if (value[index + 1] === "*") index += 1; + } + } + if (wildcardTokens > MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS) { + throw new Error( + `${label} contains more than ${MAX_NATIVE_SNAPSHOT_PATTERN_WILDCARDS} wildcard tokens` + ); + } + return value; +} +function snapshotPatterns(value, label, fallback) { + if (value === void 0) return [...fallback]; + if (!Array.isArray(value)) { + throw new Error(`${label} contains an unsafe pattern`); + } + return [...new Set(value.map((pattern) => normalizeNativeSnapshotPattern(pattern, label)))].sort( + (left, right) => left.localeCompare(right, "en") + ); +} +function positiveSnapshotInteger(value, fallback, label) { + const resolved = value ?? fallback; + if (!Number.isSafeInteger(resolved) || resolved < 1) { + throw new Error(`${label} must be a positive integer`); + } + return resolved; +} +function parseSnapshot(value) { + if (value === void 0) + return { ...DEFAULT_NATIVE_SNAPSHOT_CONFIG, include: ["**/*"], exclude: [] }; + const snapshot = record(value, "native.snapshot"); + rejectUnknown(snapshot, SNAPSHOT_KEYS, "native.snapshot"); + return { + include: snapshotPatterns( + snapshot.include, + "native.snapshot.include", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.include + ), + exclude: snapshotPatterns( + snapshot.exclude, + "native.snapshot.exclude", + DEFAULT_NATIVE_SNAPSHOT_CONFIG.exclude + ), + max_files: positiveSnapshotInteger( + snapshot.max_files, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_files, + "native.snapshot.max_files" + ), + max_total_bytes: positiveSnapshotInteger( + snapshot.max_total_bytes, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_total_bytes, + "native.snapshot.max_total_bytes" + ), + max_duration_ms: positiveSnapshotInteger( + snapshot.max_duration_ms, + DEFAULT_NATIVE_SNAPSHOT_CONFIG.max_duration_ms, + "native.snapshot.max_duration_ms" + ) + }; +} +function record(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function parsePending(value) { + if (value === void 0) return void 0; + const pending = record(value, "native.pending_root_move"); + rejectUnknown(pending, PENDING_KEYS, "native.pending_root_move"); + const id = pending.id; + const from = pending.from_artifact_root; + const to = pending.to_artifact_root; + const stage = pending.stage; + if (typeof id !== "string" || !/^[a-f0-9-]{8,}$/u.test(id)) { + throw new Error("native.pending_root_move.id is invalid"); + } + if (typeof from !== "string" || typeof to !== "string") { + throw new Error("native.pending_root_move roots must be strings"); + } + if (stage !== "copying" && stage !== "ready" && stage !== "switched") { + throw new Error("native.pending_root_move.stage is invalid"); + } + let cleanup; + if (pending.cleanup !== void 0) { + const value2 = record(pending.cleanup, "native.pending_root_move.cleanup"); + rejectUnknown(value2, CLEANUP_KEYS, "native.pending_root_move.cleanup"); + const kind = value2.kind; + const state = value2.state; + const manifestHash = value2.manifest_hash; + if (kind !== "forward-source" && kind !== "restart-staging" && kind !== "rollback-destination" && kind !== "rollback-staging") { + throw new Error("native.pending_root_move.cleanup.kind is invalid"); + } + if (state !== "prepared" && state !== "quarantined" && state !== "deleting") { + throw new Error("native.pending_root_move.cleanup.state is invalid"); + } + if (typeof manifestHash !== "string" || !/^[a-f0-9]{64}$/u.test(manifestHash)) { + throw new Error("native.pending_root_move.cleanup.manifest_hash is invalid"); + } + cleanup = { kind, state, manifestHash }; + } + return { + id, + fromArtifactRoot: normalizeArtifactRootRef(from), + toArtifactRoot: normalizeArtifactRootRef(to), + stage, + ...cleanup ? { cleanup } : {} + }; +} +function parseConfig(value) { + const root = record(value, PROJECT_CONFIG_FILE); + if (root.schema !== "comet.project.v1") throw new Error("Unsupported Comet project schema"); + if (root.default_workflow !== "native" && root.default_workflow !== "classic") { + throw new Error("default_workflow must be native or classic"); + } + const configuredWorkflows = root.workflows ?? [root.default_workflow]; + if (!Array.isArray(configuredWorkflows) || configuredWorkflows.length === 0 || configuredWorkflows.some((workflow) => workflow !== "native" && workflow !== "classic")) { + throw new Error("workflows must contain native and/or classic"); + } + const workflows = [...new Set(configuredWorkflows)]; + if (!workflows.includes(root.default_workflow)) { + throw new Error("workflows must include default_workflow"); + } + const ambientResume = root.ambient_resume ?? true; + if (typeof ambientResume !== "boolean") { + throw new Error("ambient_resume must be true or false"); + } + const native = record(root.native, "native"); + rejectUnknown(native, NATIVE_KEYS, "native"); + if (typeof native.artifact_root !== "string") { + throw new Error("native.artifact_root must be a string"); + } + const language = native.language ?? "en"; + if (language !== "en" && language !== "zh-CN") { + throw new Error("native.language must be en or zh-CN"); + } + const clarificationMode = native.clarification_mode ?? "sequential"; + if (clarificationMode !== "sequential" && clarificationMode !== "batch") { + throw new Error("native.clarification_mode must be sequential or batch"); + } + const pending = parsePending(native.pending_root_move); + const snapshot = parseSnapshot(native.snapshot); + return { + schema: "comet.project.v1", + default_workflow: root.default_workflow, + workflows, + ambient_resume: ambientResume, + native: { + artifact_root: normalizeArtifactRootRef(native.artifact_root), + language, + clarification_mode: clarificationMode, + snapshot, + ...pending ? { pending_root_move: pending } : {} + } + }; +} +async function readProjectConfig(projectRoot) { + const canonical = path10.join(projectRoot, ...PROJECT_CONFIG_FILE.split("/")); + const file = canonical; + try { + await fs10.lstat(file); + } catch (error) { + if (error.code === "ENOENT") return null; + throw error; + } + const source = (await readNativeProtectedTextFile({ + root: projectRoot, + file, + maxBytes: NATIVE_PROJECT_CONFIG_MAX_BYTES, + label: PROJECT_CONFIG_FILE + })).text; + const document = (0, import_yaml4.parseDocument)(source, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid ${PROJECT_CONFIG_FILE}: ${document.errors[0].message}`); + } + const value = document.toJS(); + if (!value || typeof value !== "object" || Array.isArray(value)) return null; + const root = value; + if (root.schema === void 0 && root.native === void 0 && root.default_workflow === void 0) { + return null; + } + return parseConfig(value); +} + +// domains/comet-native/native-lock.ts +import { AsyncLocalStorage } from "async_hooks"; +var NATIVE_LOCK_MAX_BYTES = 16 * 1024; +var nativeLockCoordinator = new AsyncLocalStorage(); + +// domains/comet-native/native-transaction.ts +import { TextDecoder as TextDecoder3 } from "util"; +var NATIVE_TRANSACTION_JOURNAL_MAX_BYTES = 256 * 1024; +var NATIVE_TRANSACTION_EVENTS_MAX_BYTES = 1024 * 1024; +var NATIVE_TRANSACTION_EVENT_MAX_BYTES = 16 * 1024; +var NATIVE_LEGACY_TRANSACTION_FILE_MAX_BYTES = 64 * 1024 * 1024; +var UTF8_DECODER = new TextDecoder3("utf-8", { fatal: true }); + +// domains/comet-native/native-snapshot.ts +var DEFAULT_NATIVE_SNAPSHOT_LIMITS = { + maxFiles: 1e4, + maxFileBytes: 5 * 1024 * 1024, + maxTotalBytes: 64 * 1024 * 1024, + maxManifestBytes: 1024 * 1024 +}; +var NATIVE_SNAPSHOT_MANIFEST_HARD_MAX_BYTES = 8 * 1024 * 1024; +var GIT_LIST_STDERR_LIMIT = 64 * 1024; +var GIT_TEXT_STDOUT_LIMIT = 64 * 1024; +var DEFAULT_NATIVE_GIT_SELECTION_LIMITS = { + maxRecords: 2e4, + maxBytes: 8 * 1024 * 1024, + maxRecordBytes: 64 * 1024 +}; +var DEFAULT_NATIVE_PHYSICAL_SELECTION_LIMITS = { + maxNodes: 2e4, + maxBytes: 8 * 1024 * 1024, + maxPathBytes: 64 * 1024 +}; +var PHYSICAL_SELECTION_SUM_MASK = (1n << 256n) - 1n; + +// domains/engine/storage-run.ts +init_state(); + +// domains/comet-native/native-run-store.ts +var NATIVE_RUN_IO_LIMITS = { + runStateBytes: 256 * 1024, + trajectoryBytes: 8 * 1024 * 1024, + trajectoryEvents: 4096, + trajectoryEventBytes: 256 * 1024, + checkpointBytes: 256 * 1024, + pendingActionBytes: 256 * 1024, + contextBytes: 1024 * 1024, + artifactsBytes: 1024 * 1024 +}; + +// domains/comet-native/native-workspace.ts +var MAX_WORKSPACE_IDENTITY_BYTES = 16 * 1024; + +// domains/comet-native/native-types.ts +var NATIVE_RUNTIME_PROTOCOL_VERSION = 3; +var NATIVE_CHANGE_SCHEMA = "comet.native.v3"; +var NATIVE_V2_CHANGE_SCHEMA = "comet.native.v2"; +var NATIVE_LEGACY_CHANGE_SCHEMA = "comet.native.v1"; + +// domains/comet-native/native-change.ts +var CHANGE_KEYS = [ + "schema", + "name", + "language", + "phase", + "brief", + "approval", + "spec_changes", + "verification_result", + "verification_report", + "archived", + "created_at", + "run_id" +]; +var LEGACY_CHANGE_KEYS = new Set(CHANGE_KEYS); +var V2_CHANGE_KEYS = /* @__PURE__ */ new Set([...CHANGE_KEYS, "minimum_runtime_version", "revision"]); +var CURRENT_CHANGE_KEYS = /* @__PURE__ */ new Set([ + ...V2_CHANGE_KEYS, + "approved_contract_hash", + "implementation_scope", + "verification_evidence", + "partial_allowance" +]); +var SPEC_CHANGE_KEYS = /* @__PURE__ */ new Set(["capability", "operation", "source", "base_hash"]); +var PHASES2 = /* @__PURE__ */ new Set(["shape", "build", "verify", "archive"]); +var APPROVALS = /* @__PURE__ */ new Set(["implicit", "confirmed"]); +var VERIFY_RESULTS2 = /* @__PURE__ */ new Set(["pending", "pass", "fail"]); +var NATIVE_CHANGE_STATE_FILE = "comet-state.yaml"; +var HASH_PATTERN = /^[a-f0-9]{64}$/u; +var NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u; +var CONTENT_ADDRESSED_REF_PATTERN = /^runtime\/evidence\/(scopes|allowances|verifications)\/([a-f0-9]{64})\.json$/u; +var NativeSchemaMigrationRequiredError = class extends Error { + constructor(change, schema) { + super( + `Native change ${change} uses ${schema}; run comet native doctor ${change} --repair before mutating it` + ); + this.change = change; + this.schema = schema; + this.name = "NativeSchemaMigrationRequiredError"; + } + change; + schema; + code = "native-schema-migration-required"; +}; +var NativeRuntimeCompatibilityError = class extends Error { + constructor(schema, minimumRuntimeVersion) { + super( + schema !== NATIVE_CHANGE_SCHEMA || minimumRuntimeVersion === null ? `Unsupported Native change schema ${schema} for runtime protocol ${NATIVE_RUNTIME_PROTOCOL_VERSION}` : `Native change ${schema} requires runtime protocol ${minimumRuntimeVersion}; current protocol is ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + this.schema = schema; + this.minimumRuntimeVersion = minimumRuntimeVersion; + this.name = "NativeRuntimeCompatibilityError"; + } + schema; + minimumRuntimeVersion; + code = "native-runtime-incompatible"; +}; +var NATIVE_BRIEF_TEMPLATE = [ + "# Outcome", + "", + "# Scope", + "", + "# Non-goals", + "", + "# Acceptance examples", + "", + "# Constraints and invariants", + "", + "# Decisions", + "", + "# Open questions", + "", + "# Verification expectations", + "" +].join("\n"); +function record2(value, label) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`${label} must be a mapping`); + } + return value; +} +function rejectUnknown2(value, known, label) { + const unknown = Object.keys(value).filter((key) => !known.has(key)); + if (unknown.length > 0) throw new Error(`${label} has unknown field(s): ${unknown.join(", ")}`); +} +function assertNativeName(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native change name: ${value}`); +} +function assertCapabilityId(value) { + if (!NAME_PATTERN.test(value)) throw new Error(`Invalid Native capability id: ${value}`); +} +function assertRelativeRef(value, label) { + if (value.length === 0 || path11.isAbsolute(value) || /^(?:[A-Za-z]:|~|[\\/])/u.test(value) || value.split(/[\\/]/u).includes("..")) { + throw new Error(`${label} must stay inside the Native change`); + } +} +function parseSpecChange(value, index) { + const item = record2(value, `spec_changes[${index}]`); + rejectUnknown2(item, SPEC_CHANGE_KEYS, `spec_changes[${index}]`); + if (typeof item.capability !== "string") throw new Error("spec change capability is required"); + assertCapabilityId(item.capability); + if (item.operation !== "create" && item.operation !== "replace" && item.operation !== "remove") { + throw new Error(`Invalid spec operation for ${item.capability}`); + } + const source = item.source; + const baseHash = item.base_hash; + if (source !== void 0 && typeof source !== "string") { + throw new Error(`Spec source for ${item.capability} must be a string`); + } + if (typeof source === "string") assertRelativeRef(source, `Spec source for ${item.capability}`); + if (item.operation === "create") { + if (!source) throw new Error(`Create spec ${item.capability} requires source`); + if (baseHash !== null) + throw new Error(`Create spec ${item.capability} requires null base_hash`); + } else if (item.operation === "replace") { + if (!source) throw new Error(`Replace spec ${item.capability} requires source`); + if (typeof baseHash !== "string" || !HASH_PATTERN.test(baseHash)) { + throw new Error(`Replace spec ${item.capability} requires a SHA-256 base_hash`); + } + } else { + if (source !== void 0) throw new Error(`Remove spec ${item.capability} forbids source`); + if (typeof baseHash !== "string" || !HASH_PATTERN.test(baseHash)) { + throw new Error(`Remove spec ${item.capability} requires a SHA-256 base_hash`); + } + } + return { + capability: item.capability, + operation: item.operation, + ...typeof source === "string" ? { source } : {}, + base_hash: baseHash + }; +} +function validDate(value) { + if (!/^\d{4}-\d{2}-\d{2}$/u.test(value)) return false; + return (/* @__PURE__ */ new Date(`${value}T00:00:00.000Z`)).toISOString().slice(0, 10) === value; +} +function parseChangeFields(root, knownKeys) { + rejectUnknown2(root, knownKeys, NATIVE_CHANGE_STATE_FILE); + if (typeof root.name !== "string") throw new Error("Native change name is required"); + assertNativeName(root.name); + if (root.language !== "en" && root.language !== "zh-CN") { + throw new Error("Native change language must be en or zh-CN"); + } + if (typeof root.phase !== "string" || !PHASES2.has(root.phase)) { + throw new Error("Native change phase is invalid"); + } + if (root.brief !== "brief.md") throw new Error("Native change brief must be brief.md"); + if (root.approval !== null && !APPROVALS.has(root.approval)) { + throw new Error("Native change approval is invalid"); + } + if (!Array.isArray(root.spec_changes)) throw new Error("Native spec_changes must be an array"); + const specChanges = root.spec_changes.map(parseSpecChange); + const duplicates = specChanges.map((change) => change.capability).filter((capability, index, all) => all.indexOf(capability) !== index); + if (duplicates.length > 0) { + throw new Error( + `Duplicate Native capability operation: ${[...new Set(duplicates)].join(", ")}` + ); + } + if (typeof root.verification_result !== "string" || !VERIFY_RESULTS2.has(root.verification_result)) { + throw new Error("Native verification_result is invalid"); + } + if (root.verification_report !== null && typeof root.verification_report !== "string") { + throw new Error("Native verification_report must be a string or null"); + } + if (typeof root.verification_report === "string") { + assertRelativeRef(root.verification_report, "Native verification_report"); + } + if (typeof root.archived !== "boolean") throw new Error("Native archived must be boolean"); + if (typeof root.created_at !== "string" || !validDate(root.created_at)) { + throw new Error("Native created_at must be a valid YYYY-MM-DD date"); + } + if (root.run_id !== null && (typeof root.run_id !== "string" || root.run_id.length === 0)) { + throw new Error("Native run_id must be a non-empty string or null"); + } + return { + name: root.name, + language: root.language, + phase: root.phase, + brief: "brief.md", + approval: root.approval, + spec_changes: specChanges, + verification_result: root.verification_result, + verification_report: root.verification_report, + archived: root.archived, + created_at: root.created_at, + run_id: root.run_id + }; +} +function parseLegacyNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_LEGACY_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_LEGACY_CHANGE_SCHEMA}`); + } + return { + schema: NATIVE_LEGACY_CHANGE_SCHEMA, + ...parseChangeFields(root, LEGACY_CHANGE_KEYS) + }; +} +function positiveInteger(value, label) { + if (!Number.isSafeInteger(value) || value < 1) { + throw new Error(`${label} must be a positive integer`); + } + return value; +} +function contentAddressedRef(value, label, kind) { + if (value === null) return null; + const match = typeof value === "string" ? CONTENT_ADDRESSED_REF_PATTERN.exec(value) : null; + if (!match || match[1] !== kind) { + throw new Error( + `${label} must be null or runtime/evidence/${kind}/.json relative to the Native change` + ); + } + return value; +} +function approvedContractHash(value) { + if (value === void 0 || value === null) return null; + if (typeof value !== "string" || !HASH_PATTERN.test(value)) { + throw new Error("Native approved_contract_hash must be null or a SHA-256 hash"); + } + return value; +} +function parseV2NativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_V2_CHANGE_SCHEMA) { + throw new Error(`Expected ${NATIVE_V2_CHANGE_SCHEMA}`); + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native v2 minimum_runtime_version" + ); + if (minimumRuntimeVersion !== 2) { + throw new Error(`Native ${NATIVE_V2_CHANGE_SCHEMA} minimum_runtime_version must be 2`); + } + return { + schema: NATIVE_V2_CHANGE_SCHEMA, + minimum_runtime_version: 2, + revision: positiveInteger(root.revision, "Native v2 revision"), + ...parseChangeFields(root, V2_CHANGE_KEYS) + }; +} +function parseNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA || root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const previous = root.schema === NATIVE_LEGACY_CHANGE_SCHEMA ? parseLegacyNativeChangeValue(root) : parseV2NativeChangeValue(root); + throw new NativeSchemaMigrationRequiredError(previous.name, previous.schema); + } + throw new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + typeof root.minimum_runtime_version === "number" ? root.minimum_runtime_version : null + ); + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion); + } + if (minimumRuntimeVersion !== NATIVE_RUNTIME_PROTOCOL_VERSION) { + throw new Error( + `Native ${root.schema} minimum_runtime_version must be ${NATIVE_RUNTIME_PROTOCOL_VERSION}` + ); + } + const revision = positiveInteger(root.revision, "Native revision"); + const fields = parseChangeFields(root, CURRENT_CHANGE_KEYS); + const approvalHash = approvedContractHash(root.approved_contract_hash); + if (fields.approval === null && approvalHash !== null) { + throw new Error("Native approved_contract_hash requires an approval"); + } + return { + schema: NATIVE_CHANGE_SCHEMA, + minimum_runtime_version: NATIVE_RUNTIME_PROTOCOL_VERSION, + revision, + ...fields, + approved_contract_hash: approvalHash, + implementation_scope: contentAddressedRef( + root.implementation_scope, + "Native implementation_scope", + "scopes" + ), + verification_evidence: contentAddressedRef( + root.verification_evidence, + "Native verification_evidence", + "verifications" + ), + partial_allowance: contentAddressedRef( + root.partial_allowance, + "Native partial_allowance", + "allowances" + ) + }; +} +function inspectNativeChangeValue(value) { + const root = record2(value, NATIVE_CHANGE_STATE_FILE); + if (root.schema === NATIVE_LEGACY_CHANGE_SCHEMA) { + const state2 = parseLegacyNativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: 1, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema === NATIVE_V2_CHANGE_SCHEMA) { + const state2 = parseV2NativeChangeValue(root); + return { + status: "migration-required", + schema: state2.schema, + minimumRuntimeVersion: state2.minimum_runtime_version, + state: state2, + message: `Native change ${state2.name} requires migration to ${NATIVE_CHANGE_SCHEMA}` + }; + } + if (root.schema !== NATIVE_CHANGE_SCHEMA) { + const minimumRuntimeVersion2 = typeof root.minimum_runtime_version === "number" && Number.isSafeInteger(root.minimum_runtime_version) ? root.minimum_runtime_version : null; + return { + status: "runtime-incompatible", + schema: typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion: minimumRuntimeVersion2, + state: null, + message: new NativeRuntimeCompatibilityError( + typeof root.schema === "string" ? root.schema : "(missing)", + minimumRuntimeVersion2 + ).message + }; + } + const minimumRuntimeVersion = positiveInteger( + root.minimum_runtime_version, + "Native minimum_runtime_version" + ); + if (minimumRuntimeVersion > NATIVE_RUNTIME_PROTOCOL_VERSION) { + return { + status: "runtime-incompatible", + schema: root.schema, + minimumRuntimeVersion, + state: null, + message: new NativeRuntimeCompatibilityError(root.schema, minimumRuntimeVersion).message + }; + } + const state = parseNativeChangeValue(root); + return { + status: "current", + schema: state.schema, + minimumRuntimeVersion: state.minimum_runtime_version, + state + }; +} +function nativeChangeDir(paths, name) { + assertNativeName(name); + const target = path11.join(paths.changesDir, name); + if (!isInsidePath(paths.changesDir, target)) throw new Error("Native change path escaped"); + return target; +} +async function hasPendingNativeSchemaMigration(paths, name) { + const file = path11.join(nativeChangeDir(paths, name), "runtime", "schema-migration.json"); + await resolveContainedNativePath(paths.nativeRoot, file); + try { + await fs11.lstat(file); + return true; + } catch (error) { + if (error.code === "ENOENT") return false; + throw error; + } +} +var NATIVE_CHANGE_DOCUMENT_MAX_BYTES = 256 * 1024; +async function readChangeDocumentFile(file, root = path11.dirname(file)) { + const ref = path11.relative(root, file).split(path11.sep).join("/"); + const source = await readNativeBoundedTextFile({ + root, + ref, + maxBytes: NATIVE_CHANGE_DOCUMENT_MAX_BYTES + }); + const document = (0, import_yaml5.parseDocument)(source.text, { uniqueKeys: true }); + if (document.errors.length > 0) { + throw new Error(`Invalid Native change file ${file}: ${document.errors[0].message}`); + } + return document.toJS(); +} +async function inspectNativeChange(paths, name) { + const file = path11.join(nativeChangeDir(paths, name), NATIVE_CHANGE_STATE_FILE); + await resolveContainedNativePath(paths.nativeRoot, file); + const inspection = inspectNativeChangeValue(await readChangeDocumentFile(file, paths.nativeRoot)); + if (inspection.state && inspection.state.name !== name) { + throw new Error(`Native change directory/name mismatch: ${name}`); + } + if (await hasPendingNativeSchemaMigration(paths, name)) { + return { + status: "migration-required", + schema: inspection.schema, + minimumRuntimeVersion: inspection.minimumRuntimeVersion, + state: inspection.state, + message: `Native schema migration is incomplete for ${name}; run doctor --repair` + }; + } + return inspection; +} +async function readNativeChange(paths, name) { + const inspection = await inspectNativeChange(paths, name); + if (inspection.status === "migration-required") { + throw new NativeSchemaMigrationRequiredError(name, inspection.schema); + } + if (inspection.status === "runtime-incompatible" || !inspection.state) { + throw new NativeRuntimeCompatibilityError(inspection.schema, inspection.minimumRuntimeVersion); + } + return inspection.state; +} + +// domains/comet-native/native-selection.ts +var NATIVE_SELECTION_MAX_BYTES = 16 * 1024; +async function readNativeSelectionRecord(paths) { + const current = await readCometCurrentSelection(paths.projectRoot); + if (current.status === "missing" || current.selection.workflow !== "native") return null; + assertNativeName(current.selection.change); + return current.selection; +} +async function resolveSelectedNativeChange(paths) { + const value = await readNativeSelectionRecord(paths); + if (!value) return null; + await readNativeChange(paths, value.change); + return value.change; +} + +// domains/comet-native/native-hook-guard.ts +function isWithin(parent, target) { + const relative = path12.relative(parent, target); + return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative); +} +function requestTargetsAreControlOnly(projectRoot, nativeRoot, request) { + return request.targets.length > 0 && request.targets.every((targetPath) => { + const target = path12.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) return true; + const relative = path12.relative(projectRoot, target).replaceAll("\\", "/"); + return relative === ".comet/config.yaml" || isWithin(nativeRoot, target); + }); +} +async function activeNativeContext(projectRoot) { + const config = await readProjectConfig(projectRoot); + if (!config || !(config.workflows ?? [config.default_workflow]).includes("native")) return null; + const paths = await nativeProjectPaths(projectRoot, config.native.artifact_root); + let entries; + try { + entries = await fs12.readdir(paths.changesDir, { withFileTypes: true }); + } catch (error) { + if (error.code === "ENOENT") return { paths, changes: [] }; + throw error; + } + const changes = []; + for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) { + if (!entry.isDirectory() || entry.isSymbolicLink()) continue; + const state = await readNativeChange(paths, entry.name); + if (!state.archived) changes.push(state); + } + return { paths, changes }; +} +async function listActiveNativeHookChanges(projectRoot) { + const context = await activeNativeContext(projectRoot); + return (context?.changes ?? []).map((change) => ({ + workflow: "native", + name: change.name, + phase: change.phase + })); +} +async function inspectNativeHookGuard(projectRoot, request, selectedChangeName) { + const context = await activeNativeContext(projectRoot); + if (!context) return { allowed: true, reason: "Native workflow is not enabled" }; + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + if (context.changes.length === 0) { + return { + allowed: true, + reason: requestTargetsAreControlOnly(projectRoot, context.paths.nativeRoot, request) ? "Native control artifact write" : "No Native changes exist" + }; + } + let change; + if (selectedChangeName) { + change = context.changes.find((candidate) => candidate.name === selectedChangeName); + if (!change) { + return { + allowed: false, + reason: `Selected Native change ${selectedChangeName} is missing or archived; resume /comet-native before retrying`, + workflow: "native", + change: selectedChangeName + }; + } + } else if (context.changes.length === 1) { + change = context.changes[0]; + } else { + const selectedName = await resolveSelectedNativeChange(context.paths); + change = context.changes.find((candidate) => candidate.name === selectedName); + if (!change) { + return { + allowed: false, + reason: "Multiple Native changes are active; select the change to resume before writing code", + workflow: "native" + }; + } + } + if (change.phase === "build") { + return { + allowed: true, + reason: "Native change is in Build", + workflow: "native", + phase: change.phase, + change: change.name + }; + } + if (request.intent === "unknown" || request.targets.length === 0) { + return { + allowed: false, + reason: `Hook write target could not be determined while Native change ${change.name} is in ${change.phase}; resume /comet-native before retrying`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + let controlTarget = false; + let externalTarget = false; + for (const targetPath of request.targets) { + const target = path12.resolve(projectRoot, targetPath); + if (!isWithin(projectRoot, target)) { + externalTarget = true; + continue; + } + const relative = path12.relative(projectRoot, target).replaceAll("\\", "/"); + if (relative === ".comet/config.yaml" || isWithin(context.paths.nativeRoot, target)) { + controlTarget = true; + continue; + } + return { + allowed: false, + reason: `Native change ${change.name} is in ${change.phase}; implementation writes are only allowed in build. Resume /comet-native to continue safely`, + workflow: "native", + phase: change.phase, + change: change.name + }; + } + return { + allowed: true, + reason: controlTarget ? "Native control artifact write" : externalTarget ? "Write target is outside the guarded project" : "No guarded write target was provided", + workflow: "native", + phase: change.phase, + change: change.name + }; +} + +// domains/comet-entry/hook-router.ts +var DEFAULT_DEPENDENCIES = { + listNative: listActiveNativeHookChanges, + listClassic: listActiveClassicHookChanges, + inspectNative: inspectNativeHookGuard, + inspectClassic: inspectClassicHookGuard +}; +function enabledWorkflows(config) { + if (!config) return ["classic"]; + return config.workflows ?? [config.default_workflow]; +} +async function resolveHookWorkflowOwner(projectRoot, dependencies = DEFAULT_DEPENDENCIES) { + const config = await readProjectConfig(projectRoot); + const enabled = enabledWorkflows(config); + let current; + try { + current = await readCometCurrentSelection(projectRoot); + } catch (error) { + return { + status: "stale", + code: "selection-unreadable", + reason: error instanceof Error ? error.message : String(error) + }; + } + if (current.status === "selected") { + const selection = current.selection; + if (!enabled.includes(selection.workflow)) { + return { + status: "stale", + code: "workflow-disabled", + reason: `selected workflow '${selection.workflow}' is not enabled for this project` + }; + } + let selectedCandidates; + try { + selectedCandidates = selection.workflow === "native" ? await dependencies.listNative(projectRoot) : await dependencies.listClassic(projectRoot); + } catch (error) { + return { + status: "stale", + code: "change-state-unreadable", + reason: `cannot safely enumerate active Comet changes: ${error instanceof Error ? error.message : String(error)}` + }; + } + const owner = selectedCandidates.find((candidate) => candidate.name === selection.change); + if (!owner) { + return { + status: "stale", + code: "target-missing", + reason: `selected ${selection.workflow} change '${selection.change}' is missing or archived` + }; + } + if (selection.workflow === "classic") { + const resolved = await resolveCurrentChange(projectRoot); + if (resolved.status !== "selected") { + return { + status: "stale", + code: "classic-selection-invalid", + reason: resolved.status === "stale" ? resolved.reason : `selected Classic change '${selection.change}' is no longer active` + }; + } + } + return { status: "owned", owner }; + } + let native; + let classic; + try { + [native, classic] = await Promise.all([ + enabled.includes("native") ? dependencies.listNative(projectRoot) : Promise.resolve([]), + enabled.includes("classic") ? dependencies.listClassic(projectRoot) : Promise.resolve([]) + ]); + } catch (error) { + return { + status: "stale", + code: "change-state-unreadable", + reason: `cannot safely enumerate active Comet changes: ${error instanceof Error ? error.message : String(error)}` + }; + } + const candidates = [...native, ...classic]; + if (candidates.length === 0) return { status: "none" }; + if (candidates.length === 1) return { status: "inferred", owner: candidates[0] }; + return { status: "ambiguous", candidates }; +} +async function inspectCometHook(projectRoot, request, dependencies = DEFAULT_DEPENDENCIES) { + if (request.intent === "non-write") { + return { allowed: true, reason: "Hook event is not a write" }; + } + try { + const resolution = await resolveHookWorkflowOwner(projectRoot, dependencies); + if (resolution.status === "none") { + return { allowed: true, reason: "No active Comet change" }; + } + if (resolution.status === "stale") { + return { + allowed: false, + reason: `${resolution.reason}. Resume /comet-native or /comet-classic and select the current change before retrying` + }; + } + if (resolution.status === "ambiguous") { + return { + allowed: false, + reason: `Multiple active Comet changes require one current selection: ${resolution.candidates.map((candidate) => `${candidate.workflow}:${candidate.name}`).join(", ")}` + }; + } + const owner = resolution.owner; + return owner.workflow === "native" ? dependencies.inspectNative(projectRoot, request, owner.name) : dependencies.inspectClassic(projectRoot, owner.name, request); + } catch (error) { + return { + allowed: false, + reason: `Comet Hook Router failed closed: ${error instanceof Error ? error.message : String(error)}` + }; + } +} + +// domains/comet-entry/hook-router-entry.ts +var USAGE = "Usage: comet-hook-router --platform [--project-root ]"; +function parseArgs(args) { + let platformId; + let projectRoot; + for (let index = 0; index < args.length; index++) { + const arg = args[index]; + if (arg === "--platform") { + platformId = args[++index]; + continue; + } + if (arg === "--project-root") { + projectRoot = args[++index]; + continue; + } + throw new Error(`Unknown argument: ${arg}`); + } + if (!platformId || platformId.startsWith("--")) throw new Error("--platform is required"); + if (!COMET_HOOK_PLATFORM_IDS.has(platformId)) { + throw new Error(`unsupported Hook platform: ${platformId}`); + } + if (projectRoot?.startsWith("--")) throw new Error("--project-root requires a value"); + return { platformId, ...projectRoot ? { projectRoot: path13.resolve(projectRoot) } : {} }; +} +async function projectRootFrom(parsed) { + if (parsed.projectRoot) return parsed.projectRoot; + const discovered = await discoverNativeProject(process.cwd()); + for (const marker of [[".comet", "config.yaml"], [".git"], ["openspec", "changes"]]) { + try { + await fs13.lstat(path13.join(discovered, ...marker)); + return discovered; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + } + let cursor = path13.resolve(process.cwd()); + while (true) { + try { + await fs13.lstat(path13.join(cursor, "openspec", "changes")); + return cursor; + } catch (error) { + if (error.code !== "ENOENT") throw error; + } + const parent = path13.dirname(cursor); + if (parent === cursor) break; + cursor = parent; + } + return null; +} +async function runCometHookRouter(args) { + let parsed; + try { + parsed = parseArgs(args); + } catch (error) { + process.stderr.write(`${error instanceof Error ? error.message : String(error)} +${USAGE} +`); + return 64; + } + let decision; + try { + const projectRoot = await projectRootFrom(parsed); + decision = projectRoot ? await inspectCometHook(projectRoot, readCometHookRequest()) : { allowed: true, reason: "No Comet project discovered" }; + } catch (error) { + decision = { + allowed: false, + reason: `Comet Hook Router failed closed during project discovery: ${error instanceof Error ? error.message : String(error)}` + }; + } + const output = renderCometHookDecision(parsed.platformId, decision); + if (output.stdout) process.stdout.write(output.stdout); + if (output.stderr) process.stderr.write(output.stderr); + return output.exitCode; +} +process.exitCode = await runCometHookRouter(process.argv.slice(2)); +export { + projectRootFrom, + runCometHookRouter +}; diff --git a/.pr-flow/.gitignore b/.pr-flow/.gitignore new file mode 100644 index 0000000..946f18f --- /dev/null +++ b/.pr-flow/.gitignore @@ -0,0 +1,2 @@ +/runs/ +/last-status.json diff --git a/.pr-flow/config.yaml b/.pr-flow/config.yaml new file mode 100644 index 0000000..e65d5d1 --- /dev/null +++ b/.pr-flow/config.yaml @@ -0,0 +1,51 @@ +defaults: + baseBranch: main + mergeStrategy: merge + reviewGate: + mode: github + hotfix: + verifyCommand: python .build-and-verify/runtime/build_and_verify.py verify --project + . --full + wait: + timeoutSeconds: 600 + pollSeconds: 15 + pr: + bodyTemplatePath: .pr-flow/pr-template.md + requiredSections: + - Summary + - Scope + - Closing References +branches: + main: + remote: origin + allowHotfixPush: true +authorization: + phraseHashAlgorithm: md5 + phraseHash: 4f9ee82bfd2ac6399a3e330ac49d75f1 +setup: + github: + branchRulesets: + - name: protect-main + targetBranches: + - main + requirePullRequest: true + requiredApprovingReviewCount: 0 + restrictDeletions: true + blockForcePushes: true + bypassForHotfix: true + statusChecks: + - task: add_or_identify + label: Full Verify + source: .build-and-verify/config.json + workflowOrJob: .github/workflows/full-verify.yml / Full Verify + purpose: Run full verification through build-and-verify runtime + failureImpact: Block PR merge + requiredChecks: true + codeScanning: + defaultSetup: true + requireCodeScanningResults: true + tool: CodeQL + threshold: github_default + allowedMergeMethods: + - merge + autoDeleteHeadBranch: false diff --git a/.pr-flow/pr-template.md b/.pr-flow/pr-template.md new file mode 100644 index 0000000..e333c23 --- /dev/null +++ b/.pr-flow/pr-template.md @@ -0,0 +1,11 @@ +## Summary + + + +## Scope + + + +## Closing References + + diff --git a/AGENTS.md b/AGENTS.md index 758a568..385c678 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,3 +11,20 @@ The five canonical triage labels use their default names. See `docs/agents/triag ### Domain docs This repository uses a single-context layout. See `docs/agents/domain.md`. + + + + + +## Comet Ambient Resume + +在这个仓库中,开始处理需要改动或调查的任务前,如果可能存在活跃 Comet workflow,把当前用户请求传入只读探针:`comet resume-probe . --stdin --json`。 + +- 如果用户通过宿主明确调用任意 Comet Skill(例如 `@comet`、`/comet`、`@comet-native` 或 `/comet-hotfix`),显式调用优先于本恢复协议;不要运行 resume probe,直接进入被调用的 Skill。 +- 只信任返回的 `workflow`、`skill` 和 `entrySource`;它们只由项目配置或无配置兼容回退决定。不得扫描或切换另一套 workflow。 +- 如果 probe 返回 `auto_resume`,简短说明选中的 active change,并进入 `nextCommand` 指向的永久入口。不要把状态命令当作恢复入口直接推进。 +- 如果 probe 返回 `ask_user`,只问一个简短问题并等待用户回复。 +- 如果当前请求未明确调用 Comet Skill,且 probe 返回 `out_of_scope` 或 `none`,不要进入 Comet workflow。 +- 如果配置或状态无效且没有 `nextCommand`,停止并报告原因;不要猜测另一个 workflow。 +- 不能只因为存在 active change 就把无关任务挂到该 change。Native 的未提交改动由 Native 入口检查,不由探针自动归因。 + diff --git a/CHANGELOG.md b/CHANGELOG.md index 5beb670..bafe2fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,367 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Changed -- Support stable Pi releases from `0.81.1` onward, with `0.82.0` added to the real-runtime qualification matrix. - -### Fixed -- Detach producer Adapter arguments and results before rendering so callbacks cannot alter tool execution, model context, or session data. -- Preserve later third-party user-message patches and keep host rendering installed across `/new`, `/resume`, and `/fork` session switches. -- Clear Bash animation timers and row state at session boundaries, and safely contain renderer component failures without a native fallback. - -## [0.6.2] - 2026-07-24 - -### Added -- Added a pure Tool Display Resolver, Pi Host Adapter, disposable producer Adapter API, and real-runtime qualification matrix for Pi 0.74.0, 0.81.1, and the development runtime (#5–#15). -- Added `builtInToolDisplays` and `overrideCallRenderer`; legacy ownership-named configuration remains accepted as display input without rewriting user files. -- Restored the capability-gated RTK compaction-hints control in `/tool-display`. - -### Changed -- Replaced tool re-registration, execution wrapping, ownership wiring, and stale historical adapters with one final tool-row display seam. Display settings no longer change active tools, definitions, schemas, ownership, execution, model context, events, or session bytes. -- Diffs now render only from trustworthy patches or explicit before/after data supplied by the original call/result. Missing write preimages and historical diffs are never read, retained, inferred, or reconstructed. -- Generic, MCP proxy/direct, same-name third-party, historical, partial, failed, aborted, truncated, image-bearing, collapsed, and expanded rows share the same display policy and native fallback. -- Updated package metadata, `/tool-display show`, settings help, configuration examples, compatibility declarations, and consumer documentation for the standalone `liuli195/pi-tool-display` repository. -- Configuration saves now preserve the documented `debug` flag; preset detection also honors the extension and debug toggles. -- Removed heuristic MCP tool detection and its global modal control. MCP-style rendering now appears only for exact `customToolOverrides` entries or explicit producer adapters; `mcpOutputMode` remains only as an adapter fallback. -- Consolidated `/tool-display` registration into one lazy production/test entry and clarified that manual `config.json` edits require `/reload`. -- Removed thinking-label message and context mutation; legacy configuration is ignored without rewriting config files or historical sessions (#6). - -### Fixed -- Cold startup, repeated `/reload`, inactive-to-active tools, and new calls now resolve current display policy on the first frame without changing the active set. -- Unsupported Pi host shapes install no partial patch, emit a concise diagnostic, and preserve native rendering and execution. -- Repeated reloads restore exact host descriptors and dispose Adapter and Bash animation state. -- Successful and partial Bash output now folds by terminal visual rows consistently with the existing command and error previews. -- Collapsed split diffs count logical content lines; split headers, trusted omission metadata, and wrapped continuations no longer consume `diffCollapsedLines`, while expanded previews remain visually bounded. -- Configuration integration tests use an isolated Pi agent directory and no longer risk rewriting the user's real configuration. - -## [0.6.1] - 2026-07-23 - -### Added -- Added independent `bashErrorOutputMode` and `bashErrorPreviewLines` settings, defaulting to a three-visual-line preview while keeping the failure header visible. - -### Fixed -- Fold long single-line Bash error output by terminal visual lines instead of source line count. - -## [0.6.0] - 2026-07-23 - -### Added -- Added `bashCommandMode` (`full`, `summary`, or `preview`) and `bashCommandPreviewLines` settings so long bash commands can be visually compacted and expanded with Ctrl+O alongside their output. -- Added the bash command display controls to the configuration modal, presets, example configuration, and usage documentation. - -## [0.5.0] - 2026-07-03 - -### Added -- Added an `enabled` config toggle that gates tool override registration, with reload cleanup that disposes overrides and patches on `session_shutdown`. ([c78163d](https://github.com/MasuRii/pi-tool-display/commit/c78163dddc0f94b7a542d2d1e01109c903bc70cc)) -- Added regression coverage for the active backlog: expanded large-diff rendering in constrained tmux-style panes (#23) and the esbuild lockfile bump from PR #24. ([7e46231](https://github.com/MasuRii/pi-tool-display/commit/7e4623191583f31a056602d8a08f1a4a7accd8b6)) - -### Changed -- Widened Pi coding-agent and Pi TUI peer dependency ranges through `^0.80.0` and added a `postinstall` patch with npm `overrides` to resolve known vulnerabilities in transitive dependencies. ([5f753a9](https://github.com/MasuRii/pi-tool-display/commit/5f753a9407fca6c2a7463d90eede01ce056c7bbc)) -- Extracted render helpers and consolidated tool override logic to reduce inline duplication. ([3239d7a](https://github.com/MasuRii/pi-tool-display/commit/3239d7a825fe14e5cdcac86c75dd4b21aec0018c)) -- Updated the lockfile-resolved `esbuild` dependency from `0.28.0` to `0.28.1` via Dependabot PR #24. ([caf65f2](https://github.com/MasuRii/pi-tool-display/commit/caf65f209c49ddcfc8362ff95c58a6a91cd1ba03)) -- Updated README badge styling and added a ko-fi support button. ([2094fb6](https://github.com/MasuRii/pi-tool-display/commit/2094fb64ca4e79ff7d947d0f8be2f2d9ea967fe2)) - -### Fixed -- Capped expanded edit/write diff bodies with the existing `expandedPreviewMaxLines` setting and a visible omission hint so large diffs stay bounded in small tmux panes (#23). Thanks to @jmikedupont2 for reporting. ([7e46231](https://github.com/MasuRii/pi-tool-display/commit/7e4623191583f31a056602d8a08f1a4a7accd8b6)) - -## [0.4.3] - 2026-06-16 - -### Added -- Added `customToolOverrides` for explicit opt-in rendering of non-built-in extension tools, with `generic` as the default kind and optional `mcp` rendering for MCP proxy-style arguments. -- Added custom tool override coverage for malformed config, output modes, late tool registration, argument shapes, and runtime contract preservation. - -### Changed -- Preserved configured MCP output mode even when MCP tools are not detected at startup, so dynamically registered MCP tools can still be decorated later. - -### Fixed -- Bash tool display overrides now preserve Pi `settings.json` shell settings (`shellPath` and `shellCommandPrefix`) when rebuilding the bash tool. - -## [0.4.2] - 2026-06-01 - -### Changed -- Deferred config modal, settings inspector, and built-in tool metadata loading until needed to reduce startup work. -- Replaced shared agent-directory lookup with a local `PI_CODING_AGENT_DIR`-aware resolver for config and capability checks. -- Widened peer dependency ranges to `^0.74.0 || ^0.75.0 || ^0.77.0 || ^0.78.0`. - -### Fixed -- Corrected classic-mode diff line-number gutter spacing. - -## [0.4.1] - 2026-05-26 - -### Added -- Reload-safe extension lifecycle: `src/disposable.ts` cleanup registry that disposes all tool overrides, prototype patches, timers, and event handlers on `session_shutdown(reason: "reload")`, preventing orphaned pi-mono default rendering after `/reload`. -- Comprehensive test suite with 15 new test files covering reload behavior, bash display, MCP overrides, ANSI utilities, diff renderer edge cases, user message boxes, thinking labels, render utilities, and integration tests (696 total tests, up from 68). - -### Fixed -- Bash display now respects `shellPath` and `commandPrefix` from `settings.json` when present (#21). -- Bash spinner timer reworked to use toolCallId-keyed Map instead of `__piToolDisplayBashSpinner`, with interval reduced from 80ms to 200ms, defensive `invalidate()` check, and all timers registered in the cleanup registry (#19). -- Bash override registration is now deferred (like read/grep/edit) and uses `before_agent_start` to discover ownership via `pi.getAllTools()` before overriding, preventing conflicts with other extensions (#17). Thanks to @iwinux for reporting. -- `pi.registerTool` is now intercepted to decorate MCP tools as they register, eliminating a race condition where `session_start`/`before_agent_start` fire before `pi-mcp-adapter` finishes registering tools (#15, #18). Thanks to @dashanlkk for reporting and opening PR #18. -- `isMcpToolCandidate()` heuristics expanded to match `mcp`, `mcp_*`, `*_mcp`, names containing `server:` or starting with `ctx_`, and parameter schemas containing `mcpServer`, `serverUrl`, or `server_name`, catching many MCP servers that were previously false negatives. -- `stripBackgroundSgrParams()` now correctly preserves foreground RGB sequences like `38;2;12;49;200m` instead of misinterpreting color component `49` as a background reset (#8, #3). Thanks to @michaelrommel for the patch and @w-winter for reporting. -- `patchUserMessageRenderPrototype` now restores stale patches from prior extension instances before re-patching, and `registerNativeUserMessageBox` has a duplicate-prevention guard with `session_shutdown` restoration (#10). OSC 133 stripping is now scoped to prompt-control sequences only; OSC 8 hyperlinks are preserved. Thanks to @w-winter for reporting. -- Thinking label duplicate-prevention guard prevents re-registering event handlers across reloads; `session_shutdown(reason: "reload")` resets the guard so re-registration works after reload; recursive nested-array handling added for malformed thinking content (#2). Thanks to @agustif for PR #2. -- `registerDeferredBuiltInToolOverrides()` is now also called on `session_start` (not just `before_agent_start`), fixing a reload bug where read/grep/edit/bash tools fell back to default pi-mono rendering. - -## [0.4.0] - 2026-05-22 - -### Added -- Added the `./tool-display-api-consumer` subpath export so other extensions can decorate tool definitions through the runtime tool-display API or queue decorations until `pi-tool-display` is loaded. -- Added hashline-anchor-aware diff rendering so read/edit anchor lines can display their `LINE#HASH` labels in the diff gutter. - -### Changed -- Deferred built-in tool override registration until the built-in owner is available and refreshed cached built-in tools on session lifecycle changes. -- Redacted secret-like debug payload values and switched debug writes to asynchronous buffered file logging. - -## [0.3.6] - 2026-05-04 - -### Added -- Documented the `debug` config flag for opt-in file diagnostics under the runtime-created `debug/` directory with terminal debug output kept disabled. -- Added regression coverage for config store isolation, pending diff preview safety, tool override registration, presets, and thinking label rendering. - -### Changed -- Scoped projected pending edit and write previews to the active workspace before reading files, with clear fallback notices when previews cannot be resolved safely. -- Shared ANSI sanitization and width-safety helpers across diff and user message rendering for more consistent narrow-pane output. - -### Fixed -- Hardened pending write metadata tracking so preview and execution state do not leak across tool call lifecycles. -- Improved tool override preview reads and write state handling for safer partial-render updates. - -## [0.3.5] - 2026-04-27 - -### Changed -- Removed the deleted bundled screenshot asset from published package contents and removed the corresponding README project-structure reference to `assets/pi-tool-display.png`. - -## [0.3.4] - 2026-04-24 - -### Added -- Added projected pending diff previews for partial `edit` and `write` tool calls so the TUI can show `pending edit`, `pending overwrite`, and `pending create` diffs before execution finishes -- Added preview fallback notices when projected edit previews cannot be resolved deterministically from the current file contents - -### Changed -- Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to `^0.70.2` -- Diff renderer write headers now support contextual action labels so pending previews can display `pending edit`, `pending overwrite`, and `pending create` - -### Fixed -- Restored native user message box spacing on recent Pi releases by extracting markdown through the newer nested `Box` wrapper and stripping OSC 133 prompt markers from fallback content normalization -- Limited fallback OSC stripping to OSC 133 prompt markers so OSC 8 hyperlinks and other non-prompt OSC sequences remain intact in user message rendering - -## [0.3.2] - 2026-04-15 - -### Added -- `diffIndicatorMode` config option with three styles: `bars` (persistent vertical indicators), `classic` (+/- markers on first row only), and `none` (no indicator column) -- Config modal dropdown for diff indicator style selection under "Diff indicators" setting - -### Changed -- Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to `^0.67.2` -- Config path resolution now uses `getAgentDir()` API to correctly respect `PI_CODING_AGENT_DIR` environment variable (thanks to @tynanbe for PR #6) -- Diff renderer now supports mode-aware indicator glyph resolution (bars, classic, none) -- Line prefix width calculations adjusted per indicator mode for accurate diff column alignment -- Removed unused `session_switch` listener from native user message box registration -- Added top margin line to native user message box rendering (thanks to @w-winter for the suggestion) -- Rebalanced diff row and inline emphasis background mixing for more consistent added/removed line readability - -### Fixed -- Diff indicator markers now render correctly across all indicator modes with proper continuation handling -- Classic mode now shows +/- only on first visual row, with spacing on wrapped continuation lines -- Corrected ANSI background reset detection so RGB color sequences containing component value `49` no longer break inline diff emphasis background rendering (thanks to @michaelrommel for reporting issue #8) - -## [0.3.1] - 2026-04-01 - -### Changed -- Updated npm keywords and package metadata for improved discoverability -- Added Related Pi Extensions cross-linking section to README - -## [0.3.0] - 2026-04-01 +## [0.1.0] - 2026-07-25 ### Added -- `prepareArguments` delegate support for built-in tool overrides (read, grep, find, ls, edit, write, bash) -- `buildPromptSnippetFromDescription()` helper to derive prompt snippets from MCP tool descriptions -- MCP proxy prompt metadata (`MCP_PROXY_PROMPT_SNIPPET`, `MCP_PROXY_PROMPT_GUIDELINES`) for tool registration -- Write execution metadata tracking via tool call ID for accurate diff rendering across execution lifecycle -- `applyLineBackgroundToWidth()` helper for consistent line background handling in diff renderer - -### Changed -- Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to ^0.64.0 -- Refactored tool-overrides to use context-based argument extraction instead of closure state -- Improved diff renderer width handling with cleaner background reset logic -- Simplified continuation prefix rendering by removing unnecessary row background parameters -- Enhanced MCP proxy tool registration with proper prompt metadata propagation - -### Fixed -- Write diff rendering now correctly tracks previous content and file existence state across render phases -- Tool call rendering now uses context-based state instead of global mutable state - -### Tests -- Added tests for diff renderer width handling with line backgrounds -- Added tests for tool-overrides configuration and prepareArguments delegation - -## [0.2.0] - 2026-03-24 - -### Added -- `bashOutputMode` config option with three modes: `opencode` (classic collapse), `summary` (line count only), `preview` (show lines) -- Live bash preview with spinner animation and elapsed time during command execution -- `bash-display.ts` module for bash call rendering with spinner state management -- `modal-icons.ts` module for Nerd Font detection and modal icon sets (`PI_NERD_FONTS` and `POWERLINE_NERD_FONTS` env vars) -- `settings-inspector-modal.ts` module with split-pane inspector UI (category list + setting details) -- Search icon in settings inspector modal filter hint (Nerd Font `\uF002` or emoji `🔍`) - -### Changed -- Settings modal now uses split-pane inspector with setting descriptions, summaries, and advanced notes -- Modal width increased to accommodate split-pane layout (wider terminals get more space) -- `showTruncationHints` config now defaults to `false` -- `showRtkCompactionHints` config now defaults to `false` -- Bash output now supports different rendering modes controlled by `bashOutputMode` -- Refactored config modal to use new inspector modal component instead of legacy settings modal - -### Config -- Added `bashOutputMode: "opencode" | "summary" | "preview"` to config schema -- Updated example config to demonstrate new `bashOutputMode` option -- Presets now include appropriate `bashOutputMode` values ("summary" for compact, "preview" for verbose) - -### Tests -- Added comprehensive tests for bash output modes (opencode, summary, preview) -- Added test coverage for spinner state management and elapsed time formatting -- Added tests for modal icon detection with various terminal environments - -## [0.1.12] - 2026-03-23 - -### Added -- `tool-metadata.ts` module with shared utilities: `toRecord`, `getTextField`, `isMcpToolCandidate`, `extractPromptMetadata` -- `cloneToolParameters` function to deep-copy built-in tool parameter schemas for extension renderers -- Comprehensive tests for MCP detection, config guards, output modes, and metadata cloning -- Plural label support in search summaries -- Conditional truncation hints via `showTruncationHints` config (defaults to `false`) -- Keywords for better npm discoverability: `hide`, `collapse`, `truncate`, `compact`, `diff`, `output-mode` - -### Changed -- Updated `@mariozechner/pi-coding-agent` and `@mariozechner/pi-tui` peer dependencies to ^0.62.0 -- Extracted shared utilities to dedicated `tool-metadata.ts` module for reuse across capabilities and tool-overrides -- Refactored tool-overrides to preserve `promptSnippet` and `promptGuidelines` on overridden read, edit, and write tools -- Improved diff renderer with accurate line number tracking and line number delta calculation for proper hunk tracking -- Removed Windows path normalization from system prompt sanitizer working-directory handling -- Simplified system prompt sanitizer to only handle documentation removal -- Enhanced package description to highlight hide/collapse/truncate capabilities for better npm discovery - -### Tests -- Added test suites for diff renderer numbering and wrap handling -- Added tests for tool-overrides config and registration behavior -- Added tests for capabilities module with MCP detection scenarios - -## [0.1.11] - 2026-03-13 - -### Changed -- Refactored `sequenceAffectsBackground` to `sequenceResetsBackground` with simpler logic that only detects background reset sequences (codes 0 and 49) - -### Tests -- Added test coverage for diff-renderer width handling - -## [0.1.10] - 2026-03-13 - -### Fixed -- Add npm override for file-type >=21.3.1 to resolve CVE (infinite loop in ASF parser) - -## [0.1.9] - 2026-03-13 - -### Added -- Write overwrite diff guard to skip expensive diff computation for large files (4000+ lines or 1M+ cells) -- Cached user message markdown renderer to avoid rebuilding markdown for large content on every render -- Configurable limits for user message markdown (100K characters, 2000 lines) - -### Changed -- Improved performance for large write operations by using approximate stats instead of computing full diff -- User message markdown now bypasses rebuild for extremely large content (>100K chars or >2000 lines) -- Optimized write diff rendering to defer detailed data computation until actually needed - -### Fixed -- Prevented UI slowdown on very large file writes by showing guard message instead of computing expensive diffs -- Avoided redundant markdown parser instantiation for repeated renders of the same user message - -## [0.1.8] - 2026-03-12 - -### Changed -- Extracted diff presentation logic into dedicated `diff-presentation.ts` module with `DiffPresentationMode`, `buildDiffSummaryText`, `normalizeDiffRenderWidth`, and `resolveDiffPresentationMode` utilities -- Improved compact line rendering with dedicated marker and prefix functions -- Added width-safe diff rendering utilities for consistent terminal width handling - -## [0.1.7] - 2026-03-07 - -### Added -- Added line-width safety utilities for diff rendering so collapsed and expanded diff output can be clamped to the current pane width. -- Added utility test coverage for write display helpers, native user message box helpers, and narrow-width diff hint behavior. - -### Changed -- Updated README documentation to reflect the current command surface, config model, width-safe diff behavior, native user message box pipeline, and project structure. -- Refactored native user message box rendering into focused markdown, patching, renderer, and ANSI/background utility modules. - -### Fixed -- Prevented diff rendering and collapsed diff hints from overflowing narrow terminal widths by progressively shortening hint text and clamping rendered lines. -- Preserved inline `write` call summaries with line-count and byte-size metadata when content is available. -- Prevented thinking label presentation changes from leaking into future assistant context by sanitizing stored thinking blocks during the `context` extension event. -- Hardened thinking label normalization to strip ANSI residue fragments such as `38;5;208m` before display formatting. -- Restored final-message thinking label persistence on `message_end` so themed labels remain consistent after streaming and across session reloads. -- Improved native user message box rendering so markdown content, ANSI-only blank lines, and background fill behave more consistently. - -## [0.1.6] - 2026-03-04 - -### Fixed -- Use absolute GitHub raw URL for README image to fix npm display - -## [0.1.5] - 2026-03-04 - -### Added -- Thinking labels feature that prefixes AI reasoning blocks with themed "Thinking:" labels for better readability - -### Changed -- Rewrote README.md with professional documentation standards -- Added comprehensive feature documentation, configuration reference, and usage examples -- Simplified settings modal by removing less-used advanced options (expandedPreviewMaxLines, diffSplitMinWidth, diffCollapsedLines, tool ownership toggles) - -## [0.1.4] - 2026-03-02 - -### Added -- Auto-detection of MCP and RTK capabilities to conditionally expose related UI/config controls. - -### Changed -- `/tool-display` modal now hides MCP settings when MCP tooling is unavailable. -- `/tool-display` modal now hides RTK compaction hint settings when RTK optimizer is unavailable. -- Runtime rendering now force-disables MCP output mode and RTK hint rendering when those capabilities are unavailable. -- Native user message box is now user-configurable via `enableNativeUserMessageBox` in config and `/tool-display` settings. - -## [0.1.3] - 2026-03-02 - -### Added -- Added per-tool ownership config via `registerToolOverrides` for `read`, `grep`, `find`, `ls`, `bash`, `edit`, and `write` so users can avoid tool ownership conflicts with other extensions. -- Added settings modal toggles for built-in tool ownership and `/reload` guidance when ownership changes. -- Added backward-compatible config migration from legacy `registerReadToolOverride` to `registerToolOverrides.read`. - -### Changed -- Built-in tool override registration is now conditional per tool based on ownership settings. -- Updated README configuration/troubleshooting docs for multi-tool extension compatibility. - -## [0.1.2] - 2026-03-01 - -### Fixed -- Corrected `write` call rendering state handling so path changes without new content no longer reuse stale line/size metadata from previous writes. -- Restored write call suffix rendering (`(X lines, Y)`) when content is available, improving call summary consistency. - -## [0.1.1] - 2026-03-01 - -### Changed -- Reorganized repository layout to a cleaner package structure: - - moved implementation modules to `src/` - - moved screenshot assets to `assets/` - - moved example config to `config/` - - kept root `index.ts` as stable Pi auto-discovery entrypoint. -- Simplified TypeScript build command to use `tsconfig.json` project mode. -- Updated README installation heading now that npm package is published. - -## [0.1.0] - 2026-03-01 - -### Added -- Public repository scaffolding (`README.md`, `LICENSE`, `CHANGELOG.md`, `.gitignore`, `.npmignore`). -- Package metadata for public distribution (`keywords`, `files`, `license`, `publishConfig`, engine constraints). -- Vendored `zellij-modal.ts` to keep this extension self-contained as a standalone repository. - -### Changed -- Updated `config-modal.ts` to use local `zellij-modal.ts` import. -- Updated build script to include `zellij-modal.ts`. +- First standalone release as `@plus/pi-tool-display`. +- Compact Pi tool rows, trustworthy tool-provided diffs, display presets, and configurable output modes. +- Support for stable Pi releases from `0.81.1` onward. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..041de4b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,14 @@ +# Contributing + +1. Fork the repository and create a branch from `main`. +2. Install dependencies with `npm ci`. +3. Make a focused change with tests where behavior changes. +4. Run: + +```bash +python .build-and-verify/runtime/build_and_verify.py verify --project . +``` + +5. Open a pull request using the repository template. + +Strict real-runtime qualification additionally requires the `PI_RUNTIME_*` paths documented in `README.md`. diff --git a/LICENSE b/LICENSE index 4ff8a1f..991d27a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2026 MasuRii +Copyright (c) 2026 liuli195 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 08c620a..3f74888 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@
-# pi-tool-display +# @plus/pi-tool-display -[![npm version](https://img.shields.io/npm/v/pi-tool-display?style=for-the-badge)](https://www.npmjs.com/package/pi-tool-display) [![License](https://img.shields.io/github/license/liuli195/pi-tool-display?style=for-the-badge)](LICENSE) [![Platform](https://img.shields.io/badge/Platform-macOS%20%7C%20Linux%20%7C%20Windows-blue?style=for-the-badge)]() -[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Y8Y01PSSVR) - A pure TUI display wrapper for the [Pi coding agent](https://github.com/mariozechner/pi). -`pi-tool-display` keeps tool rows compact, renders trustworthy tool-provided diffs, and improves the native user prompt box without changing tools, model context, messages, or sessions. +This extension is derived from [MasuRii/pi-tool-display](https://github.com/MasuRii/pi-tool-display). + +`@plus/pi-tool-display` keeps tool rows compact, renders trustworthy tool-provided diffs, and improves the native user prompt box without changing tools, model context, messages, or sessions. image @@ -35,7 +34,7 @@ A pure TUI display wrapper for the [Pi coding agent](https://github.com/mariozec - **Per-tool display toggles** that never change tool ownership or execution - **Explicit third-party rendering** through `customToolOverrides` or producer adapters; MCP-like tools are never auto-detected for styling - **Capability-aware RTK settings** that appear only when the optimizer is available -- **Adapter API for renderer consumers** through the `pi-tool-display/tool-display-api-consumer` subpath export +- **Adapter API for renderer consumers** through the `@plus/pi-tool-display/tool-display-api-consumer` subpath export ## Installation @@ -51,16 +50,10 @@ Place this folder in one of Pi's auto-discovery locations: .pi/extensions/pi-tool-display ``` -### npm package - -```bash -pi install npm:pi-tool-display -``` - ### Git repository ```bash -pi install git:github.com/liuli195/pi-tool-display +pi install git:github.com/liuli195/pi-tool-display@v0.1.0 ``` ## Usage @@ -97,10 +90,10 @@ JSON-only controls include the extension master switch, debug logging, built-in ### Tool display adapter API -Other extensions can opt into `pi-tool-display` rendering without directly depending on its load order by importing the consumer helper: +Other extensions that declare `@plus/pi-tool-display` as a direct dependency can opt into its rendering without depending on load order by importing the consumer helper: ```ts -import { registerRendererAdapter } from "pi-tool-display/tool-display-api-consumer"; +import { registerRendererAdapter } from "@plus/pi-tool-display/tool-display-api-consumer"; const dispose = registerRendererAdapter({ id: "my-extension:mcp", @@ -117,7 +110,11 @@ The deprecated `decorateToolForDisplay(tool, adapter)` migration facade register ## Compatibility -Supported Pi versions are `0.74.0`, `0.80.3` (the repository development runtime), and stable releases from `0.81.1` onward. The release matrix exercises `0.74.0`, `0.81.1`, `0.82.0`, and the development runtime. Older, prerelease, or incompatible private TUI shapes emit one concise debug diagnostic and keep Pi's native rendering and execution. +Supported Pi releases are `0.81.1` and later stable versions. The release matrix currently qualifies `0.81.1`, `0.82.0`, and the development runtime as representative points. + +Versions below `0.81.1` and prerelease version strings are **not supported**. The peer dependency range and Host Adapter gate accept stable releases from `0.81.1` onward; the qualification matrix does not limit that support range. + +Older, prerelease, or incompatible private TUI shapes emit one concise debug diagnostic and keep Pi's native rendering and execution. ## Presets @@ -150,6 +147,18 @@ Actual global path: $PI_CODING_AGENT_DIR/extensions/pi-tool-display/config.json A starter template is included at `config/config.example.json`. +### Project-local config + +Trusted projects can override global display settings by placing a `config.json` in their project-local extension directory: + +```text +/.pi/extensions/pi-tool-display/config.json +``` + +Project-local config is read-only and only loaded when `isProjectTrusted()` is active (i.e., when the user has approved project-local files). It overlays the global config: any fields defined in the project config override the corresponding global values. Write operations through `/tool-display` always save to the global config. + +The `.pi` directory name uses Pi's `CONFIG_DIR_NAME` constant and is not hardcoded. + ### Configuration options | Option | Type | Default | Description | @@ -163,7 +172,7 @@ A starter template is included at `config/config.example.json`. | `searchOutputMode` | string | `"hidden"` | `hidden`, `count`, or `preview` | | `mcpOutputMode` | string | `"hidden"` | Fallback `hidden`, `summary`, or `preview` mode for explicitly registered MCP producer adapters; it does not discover or opt tools into rendering | | `previewLines` | number | `8` | Lines shown in collapsed preview mode | -| `expandedPreviewMaxLines` | number | `4000` | Max expanded source lines for read/search/MCP/Bash previews; expanded diffs use it as a rendered-row bound | +| `expandedPreviewMaxLines` | number | `4000` | Max expanded visual rows for non-Diff previews; for Diff it caps logical Diff lines | | `bashOutputMode` | string | `"opencode"` | `opencode` (collapse), `summary` (line count), or `preview` (show lines) | | `bashCollapsedLines` | number | `10` | Visual rows shown for collapsed Bash output in opencode mode | | `bashCommandMode` | string | `"preview"` | Bash command display: `full`, `summary`, or `preview` | @@ -303,7 +312,7 @@ Debug logging is disabled by default. Set `debug` to `true` in the extension roo ### Edit and write diffs -`edit` and `write` results use the same diff renderer. In `auto` mode the extension chooses split or unified layout based on available width. Collapsed limits count logical diff content lines, so split headers, trusted omission metadata, and wrapped continuations do not consume the budget. Expanded limits still count rendered rows to keep small panes bounded. +`edit` and `write` results use the same diff renderer. In `auto` mode the extension chooses split or unified layout based on available width. Collapsed and expanded limits count logical Diff content lines, so headers, trusted omission metadata, and wrapped continuations do not consume the body budget. The extra omission hint reports the omitted visual Diff lines. Partial `edit` calls can render a diff only from explicit old/new text supplied by the call. `write` calls show neutral content summaries unless the tool supplies trustworthy diff evidence. Rendering never reads the workspace to reconstruct a preimage or infer create/overwrite semantics. @@ -387,7 +396,7 @@ pi-tool-display/ │ └── config.example.json # Starter config template └── tests/ ├── ansi-utils.test.ts # ANSI utility tests including foreground RGB preservation - ├── bash-display.test.ts # Bash display and spinner tests + ├── bash-display.test.ts # Deterministic Bash display tests ├── capabilities-edge.test.ts # Capability detection edge cases ├── config-modal.test.ts # Config modal tests ├── custom-tool-overrides.test.ts # Opt-in custom tool override tests @@ -410,20 +419,18 @@ npm run build # Run the local real-runtime contract (missing optional runtimes are skipped) npm run test:contract:local -# Full four-runtime matrix and complete verification -# See docs/ownership-verification.md for required PI_RUNTIME_* roots. -npm test -npm run typecheck +# Standard local verification (missing optional Pi runtimes are skipped) +npm run check +npm run test:contract:local npm run build git diff --check -``` -## Related Pi Extensions - -- [pi-image-tools](https://github.com/MasuRii/pi-image-tools) — Image attachment and inline preview for the Pi TUI -- [pi-hide-messages](https://github.com/MasuRii/pi-hide-messages) — Hide older chat messages without losing context -- [pi-startup-redraw-fix](https://github.com/MasuRii/pi-startup-redraw-fix) — Fix terminal redraw glitches on startup -- [pi-permission-system](https://github.com/MasuRii/pi-permission-system) — Permission enforcement for tool and command access +# Strict runtime qualification (requires every runtime root below) +PI_RUNTIME_DEV_ROOT=/path/to/pi-dev \ +PI_RUNTIME_0_81_1_ROOT=/path/to/pi-0.81.1 \ +PI_RUNTIME_0_82_0_ROOT=/path/to/pi-0.82.0 \ +npm run test:contract:required +``` ## License diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 0000000..996f3fe --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,20 @@ +# @plus/pi-tool-display 0.1.0 + +The first standalone release of `@plus/pi-tool-display` provides compact Pi tool rows and trustworthy tool-provided diff rendering without changing tool execution, model context, messages, or sessions. + +## Highlights + +- Compact rendering for Pi's built-in tools +- Configurable hidden, summary, preview, and expanded output modes +- Split and unified diff layouts +- Explicit adapters for third-party tool renderers +- Reload-safe lifecycle and project-local configuration overlays +- Support for stable Pi releases from `0.81.1` onward + +## Install + +```bash +pi install git:github.com/liuli195/pi-tool-display@v0.1.0 +``` + +See [README.md](README.md) for configuration and usage. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..999519a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +## Supported versions + +Security fixes are provided for the latest GitHub release. + +## Reporting a vulnerability + +Please use GitHub's private vulnerability reporting for this repository. Do not open a public issue for an unpatched vulnerability. + +Include affected versions, impact, reproduction steps, and any suggested mitigation. You can expect an initial response within seven days. diff --git a/docs/agents/domain.md b/docs/agents/domain.md index 4fb0685..43da489 100644 --- a/docs/agents/domain.md +++ b/docs/agents/domain.md @@ -1,29 +1,27 @@ # Domain Docs -Engineering skills should use this repository’s domain documentation when exploring the codebase. +工程技能探索代码库时,应按以下规则读取领域文档。 -## Before exploring, read these +## 当前结构 -- `CONTEXT.md` at the repository root. -- Relevant ADRs under `docs/adr/`. +本仓库采用 single-context 布局。 -If these files do not exist, proceed silently. Create them lazily through the domain-modeling workflow when terminology or architectural decisions are resolved. +- 当前没有 `CONTEXT.md`;不存在时静默继续。 +- 规格与架构约束位于 `docs/comet/specs/`,替代 `docs/adr/`。 -## Layout +## 探索前读取 -This is a single-context repository: +读取与任务相关的规格: -``` -/ -├── CONTEXT.md -├── docs/adr/ -└── src/ -``` +- `docs/comet/specs/pure-display-tool-rendering.md` +- `docs/comet/specs/visual-preview-and-config-lifecycle-corrections.md` -## Vocabulary +以后新增规格时,也应检查 `docs/comet/specs/` 下的相关文件。 -Use terminology defined in `CONTEXT.md`. Avoid synonyms that the glossary explicitly rejects. If a required concept is absent, reconsider the terminology or record the gap for domain modeling. +## 词汇 -## ADR conflicts +若根目录以后出现 `CONTEXT.md`,使用其中定义的领域词汇,避免使用其明确排斥的同义词。 -Explicitly identify output that contradicts an existing ADR instead of silently overriding it. +## 规格冲突 + +如果输出与现有规格冲突,必须明确指出相关规格及冲突内容,不得静默覆盖。 diff --git a/docs/agents/issue-tracker.md b/docs/agents/issue-tracker.md index 9696f61..82cfbf5 100644 --- a/docs/agents/issue-tracker.md +++ b/docs/agents/issue-tracker.md @@ -1,21 +1,45 @@ # Issue tracker: GitHub -Issues and PRDs for this repo live in `liuli195/pi-tool-display` GitHub Issues. Use the `gh` CLI with `--repo liuli195/pi-tool-display`. +Issues and PRDs for this repo live as GitHub issues. Use the `gh` CLI for all operations. ## Conventions -- **Create an issue**: `gh issue create --repo liuli195/pi-tool-display --title "..." --body "..."` -- **Read an issue**: `gh issue view --repo liuli195/pi-tool-display --comments` -- **List issues**: `gh issue list --repo liuli195/pi-tool-display --state open` -- **Comment**: `gh issue comment --repo liuli195/pi-tool-display --body "..."` -- **Apply/remove labels**: `gh issue edit --repo liuli195/pi-tool-display --add-label "..."` / `--remove-label "..."` -- **Close**: `gh issue close --repo liuli195/pi-tool-display --comment "..."` +- **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies. +- **Read an issue**: `gh issue view --comments`, filtering comments by `jq` and also fetching labels. +- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters. +- **Comment on an issue**: `gh issue comment --body "..."` +- **Apply / remove labels**: `gh issue edit --add-label "..."` / `--remove-label "..."` +- **Close**: `gh issue close --comment "..."` + +Infer the repo from `git remote -v` — `gh` does this automatically when run inside a clone. ## Pull requests as a triage surface -**PRs as a request surface: no.** +**PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; `/triage` reads this flag.)_ + +When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents: + +- **Read a PR**: `gh pr view --comments` and `gh pr diff ` for the diff. +- **List external PRs for triage**: `gh pr list --state open --json number,title,body,labels,author,authorAssociation,comments` then keep only `authorAssociation` of `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, or `NONE` (drop `OWNER`/`MEMBER`/`COLLABORATOR`). +- **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`. + +GitHub shares one number space across issues and PRs, so a bare `#42` may be either — resolve with `gh pr view 42` and fall back to `gh issue view 42`. + +## When a skill says "publish to the issue tracker" + +Create a GitHub issue. + +## When a skill says "fetch the relevant ticket" + +Run `gh issue view --comments`. + +## Wayfinding operations -## Skill operations +Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets. -- When a skill says **publish to the issue tracker**, create a GitHub issue in `liuli195/pi-tool-display`. -- When a skill says **fetch the relevant ticket**, run `gh issue view --repo liuli195/pi-tool-display --comments`. +- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`. +- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #` at the top of the child body. Labels: `wayfinder:` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev. +- **Blocking**: GitHub's **native issue dependencies** — the canonical, UI-visible representation. Add an edge with `gh api --method POST repos///issues//dependencies/blocked_by -F issue_id=`, where `` is the blocker's numeric **database id** (`gh api repos///issues/ --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only — the live gate). Where dependencies aren't available, fall back to a `Blocked by: #, #` line at the top of the child body. A ticket is unblocked when every blocker is closed. +- **Frontier query**: list the map's open children (`gh issue list --state open`, scoped to the map's sub-issues / task list), drop any with an open blocker (`issue_dependencies_summary.blocked_by > 0`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins. +- **Claim**: `gh issue edit --add-assignee @me` — the session's first write. +- **Resolve**: `gh issue comment --body ""`, then `gh issue close `, then append a context pointer (gist + link) to the map's Decisions-so-far. diff --git a/docs/agents/triage-labels.md b/docs/agents/triage-labels.md index 9148b88..b716855 100644 --- a/docs/agents/triage-labels.md +++ b/docs/agents/triage-labels.md @@ -1,11 +1,15 @@ # Triage Labels -| Canonical role | Tracker label | -| --- | --- | -| `needs-triage` | `needs-triage` | -| `needs-info` | `needs-info` | -| `ready-for-agent` | `ready-for-agent` | -| `ready-for-human` | `ready-for-human` | -| `wontfix` | `wontfix` | - -When a skill mentions a canonical triage role, use the corresponding tracker label from this table. +The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker. + +| Label in mattpocock/skills | Label in our tracker | Meaning | +| -------------------------- | -------------------- | ---------------------------------------- | +| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue | +| `needs-info` | `needs-info` | Waiting on reporter for more information | +| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent | +| `ready-for-human` | `ready-for-human` | Requires human implementation | +| `wontfix` | `wontfix` | Will not be actioned | + +When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table. + +Edit the right-hand column to match whatever vocabulary you actually use. diff --git a/docs/spec/pure-display-tool-rendering.md b/docs/comet/specs/pure-display-tool-rendering.md similarity index 99% rename from docs/spec/pure-display-tool-rendering.md rename to docs/comet/specs/pure-display-tool-rendering.md index 0c2bdb7..5da2579 100644 --- a/docs/spec/pure-display-tool-rendering.md +++ b/docs/comet/specs/pure-display-tool-rendering.md @@ -1,6 +1,6 @@ # Spec: Pure Display Tool Rendering -> Status: Approved direction; implementation pending +> Status: Partially implemented (v0.7.0) > > Scope: Refactor `pi-tool-display` into a pure TUI display wrapper and tool beautifier. diff --git a/docs/comet/specs/visual-preview-and-config-lifecycle-corrections.md b/docs/comet/specs/visual-preview-and-config-lifecycle-corrections.md new file mode 100644 index 0000000..3fc5d4a --- /dev/null +++ b/docs/comet/specs/visual-preview-and-config-lifecycle-corrections.md @@ -0,0 +1,123 @@ +# Spec: Visual Preview and Configuration Lifecycle Corrections + +> Status: Implemented in v0.7.1 +> +> Scope: Complete the surviving upstream feedback work without violating the pure-display rendering contract. + +## Problem Statement + +Users expect compact tool previews to occupy a predictable amount of terminal space. Today non-Diff previews still mix logical-line preprocessing with visual-row limiting, so wrapped output can report the wrong omission count or bypass the intended budget. Some paths also emit logical-line omission hints even though users care about the terminal rows hidden from view. + +Project-local configuration and renderer lifecycle behavior are also incomplete. A read-only project overlay can leak into global configuration writes, nested custom-tool settings can be reset by partial overlays, and disabling the extension does not reliably dispose every installed display patch. Runtime support declarations additionally disagree with the versions accepted by the Pi Host Adapter. + +These gaps mean the four surviving upstream feedback categories cannot yet be considered complete: visual preview budgeting, Bash spinner removal, supported Pi compatibility, and trusted project-local configuration. + +## Solution + +All non-Diff previews will pass complete display text to one final visual-preview component. That component will render at the actual terminal width, apply a visual-row body budget, and append at most one omission hint reporting omitted visual rows. The hint is outside the body budget and occupies one additional row. + +Diff remains the exception: its body budget is expressed in logical Diff lines. After those logical Diff lines are selected and rendered, the omission hint reports the visual Diff rows omitted by the logical selection. + +Configuration updates will be explicit patches rather than reconstructed differences from an effective merged configuration. Trusted project configuration remains a read-only overlay and can never be persisted through global commands. Nested custom-tool overrides will merge field-by-field. Every installed Host Adapter, Resolver registration, and native user-message patch will expose and use an owned disposer so `enabled:false`, reload, shutdown, and project/session transitions restore native rendering. + +The package peer range, Host Adapter runtime gate, documentation, and runtime matrix will describe one consistent support contract. Removed Bash animation timers remain removed. + +## User Stories + +1. As a Pi user, I want `previewLines` to limit terminal rows rather than newline-delimited source lines, so that a single long line cannot fill the viewport. +2. As a Pi user, I want Bash success previews to use a visual-row budget, so that wrapped output remains compact. +3. As a Pi user, I want Bash error previews to use the same visual-row contract, so that failures remain readable without flooding the viewport. +4. As a Pi user, I want partial Bash output to use the same budget as completed output, so that a running command does not expand unpredictably. +5. As a Pi user, I want Read previews to respect terminal wrapping, so that long file lines remain bounded. +6. As a Pi user, I want Grep, Find, and Ls previews to respect terminal wrapping, so that search results remain compact at narrow widths. +7. As a Pi user, I want MCP and custom-tool previews to respect terminal wrapping, so that third-party output follows the same display policy. +8. As a Pi user, I want expanded preview caps to count visual rows, so that expansion remains safe for very large output. +9. As a Pi user, I want folded non-Diff previews to show one omission hint, so that truncation information is clear rather than duplicated. +10. As a Pi user, I want the omission hint to report omitted visual rows, so that its number matches what the terminal would otherwise display. +11. As a Pi user, I want the omission hint outside the body budget, so that a budget of one still displays one body row. +12. As a Pi user, I want singular and plural hint wording to be correct, so that one omitted row is not described as multiple rows. +13. As a Pi user, I want narrow panes to retain an understandable omission marker, so that truncation is not silently hidden. +14. As a Pi user, I want empty successful output to continue showing `(no output)`, so that an empty renderer is distinguishable from missing UI. +15. As a Pi user, I want Diff folding to continue counting logical Diff lines, so that wrapping does not change which source changes are selected. +16. As a Pi user, I want a folded Diff hint to report the visual Diff rows omitted by the logical selection, so that I understand the viewport impact of hidden Diff content. +17. As a Pi user, I want collapsed and expanded Diff modes to share the logical Diff-line contract, so that expansion caps are predictable. +18. As a Pi user, I want tool-supplied truncation metadata to remain visible separately from display omission, so that display folding does not imply the tool returned complete data. +19. As a Pi user, I want project-local configuration to override global display settings only in that trusted project, so that teams can share presentation preferences safely. +20. As a Pi user, I want untrusted projects ignored, so that repository files cannot silently alter extension behavior. +21. As a Pi user, I want global commands to persist only fields I explicitly changed, so that a project overlay never leaks into global configuration. +22. As a Pi user, I want global presets and resets to remain complete under a project overlay, so that project values do not suppress intended global writes. +23. As a Pi user, I want a partial custom-tool project override to preserve its global sibling fields, so that changing output mode does not enable or reclassify the tool. +24. As a Pi user, I want `enabled:false` to restore native rendering immediately, so that disabling the extension is reversible. +25. As a Pi user, I want switching projects or sessions not to retain stale renderers, so that one project's configuration cannot affect another. +26. As a Pi user, I want reload and shutdown to restore exactly the patches owned by this installation, so that repeated lifecycle transitions do not stack wrappers. +27. As a Pi user, I want unsupported Pi runtimes to fall back to native rendering, so that display compatibility cannot affect tool execution. +28. As a Pi user, I want package installation claims to match runtime behavior, so that a peer-compatible Pi release is not unexpectedly rejected by the Host Adapter. +29. As a Pi user, I want Bash history rows not to animate on timers, so that scrolling does not jump or flicker. +30. As a maintainer, I want one visual-preview seam for all non-Diff tools, so that budgeting and hint behavior cannot diverge by renderer. +31. As a maintainer, I want complete source text passed to the visual-preview seam, so that omitted visual-row counts are exact. +32. As a maintainer, I want Diff budgeting isolated from generic preview budgeting, so that its logical-line exception remains explicit. +33. As a maintainer, I want configuration commands to submit patches, so that persistence does not infer intent from merged state. +34. As a maintainer, I want each display installation to own a disposer, so that lifecycle cleanup is directly testable. +35. As a maintainer, I want visual behavior tested through final component rendering, so that tests exercise real wrapping rather than helper internals. +36. As a maintainer, I want real-runtime contracts to run for every declared support target, so that skipped environments are not reported as passing. +37. As a maintainer, I want documentation and the upstream survivor audit to reflect verified behavior, so that completed and remaining work are accurately reported. +38. As a maintainer, I want the extension to remain a pure display wrapper, so that none of these corrections alter tools, Agent context, messages, execution, results, or session bytes. + +## Implementation Decisions + +1. The final component render boundary is the sole non-Diff visual-budget seam. +2. Non-Diff callers provide complete sanitized display text and never pre-slice it with a logical-line helper. +3. A preview body budget counts only rendered body rows. An omission hint, when needed, is appended as one additional row. +4. Collapsed non-Diff hints use the form `N more visual line(s) • Ctrl+O to expand` and occur at most once. +5. Expanded non-Diff caps report the actual omitted visual-row count and use consistent visual-row terminology. +6. Tool/backend truncation metadata is a separate informational hint and is not folded into the display omission count. +7. Extremely narrow widths use a width-safe abbreviated omission marker rather than emitting over-width text or silently omitting the fact of truncation. +8. Diff body selection uses logical Diff-line identities in collapsed and expanded modes. +9. Diff omission accounting renders the complete and selected logical Diff sets at the same width and reports the difference in visual Diff rows. +10. The Diff hint is outside the logical Diff body budget. +11. Empty-output behavior remains explicit and is not treated as truncation. +12. Project configuration is read only after trust confirmation and is retained separately from global configuration. +13. Configuration mutation accepts an explicit partial patch or explicit preset/reset operation; it does not infer user intent by comparing complete merged objects. +14. Nested built-in and custom-tool settings merge at their field level, preserving unspecified siblings. +15. Host Adapter installation, Resolver registration, and native user-message patch installation each return owned idempotent disposers. +16. The extension owns a per-session installation aggregate that is disposed on disablement, session transition, reload, shutdown, and replacement. +17. Disposal restores only descriptors still owned by the current installation and preserves later foreign patches. +18. The runtime support policy must be represented identically in peer dependencies, Host Adapter checks, runtime tests, README, and diagnostics. +19. Bash spinner and elapsed-time timers remain absent; no history-row interval invalidation is reintroduced. +20. All changes remain presentation-only and preserve original tool data, execution, Agent context, event ordering, and session serialization. +21. Producer Renderer Adapter registrations are extension-load-scoped display intent, not session/project installations. They persist across ordinary session transitions, read the current effective configuration at render time, and are disposed on extension reload or quit. Host/Resolver attachment and native user-message prototype patches remain session-owned. + +## Testing Decisions + +1. Tests assert external behavior through final `Component.render(width)` output rather than private line-splitting helpers. +2. The primary preview seam covers Bash success/error/partial, Read, Search, MCP, and Custom renderers with the same worked examples. +3. Width coverage includes 1, 2, 5, 10, 20, 40, 80, 120, and a very wide terminal. +4. Content coverage includes empty output, multiple short logical lines, one extremely long logical line, mixed wrapped and unwrapped lines, ANSI, Unicode, CJK, and emoji. +5. Budget coverage includes zero where supported, one, two, defaults, small expanded caps, and unlimited expanded mode. +6. Tests assert exact body-row count, exact omitted visual-row number, a single display omission hint, and width-safe output. +7. Diff tests assert logical Diff body selection independently from the visual Diff-row omission number at multiple widths. +8. Tool-provided truncation metadata is tested independently from display omission hints. +9. Configuration tests use the public command/modal update seam and verify persisted global JSON plus effective merged behavior. +10. Configuration cases include trusted and untrusted projects, one-field edits, presets, resets, nested custom-tool fields, project transitions, and fresh sessions. +11. Lifecycle tests observe the host renderer before installation, while enabled, and after disable/disposal; they do not assert internal cleanup arrays. +12. Real-runtime contracts cover development and every declared supported release and must fail clearly when required runtime roots are unavailable in qualification runs. +13. Existing pure-display byte-invariance and no-tool-registration tests remain mandatory. +14. Each behavior is implemented with a red-green vertical slice, followed by typechecking and the relevant single test file. +15. Final qualification includes the full suite, build, diff check, runtime matrix, and parallel Standards and Spec reviews. + +## Out of Scope + +- Changing tool execution, ownership, arguments, results, Agent context, messages, or session content. +- Reintroducing Bash spinner animation or elapsed-time polling. +- Adding project-local write commands; project configuration remains read-only. +- Supporting historical Pi releases below the explicitly selected support floor unless separately qualified. +- Reconstructing missing diffs or reading workspace files for presentation. +- Redesigning colors, Diff algorithms, presets, or unrelated modal behavior. +- Treating missing real-runtime environments as successful qualification. + +## Further Notes + +- The surviving upstream categories are #36 visual preview budgeting, #14/#19 Bash timer repainting, #20/#31 Pi compatibility, and PR #27 trusted project-local configuration. +- Spinner removal is implemented at the code level but still requires final regression qualification. +- The existing visual implementation is not a safe base to preserve wholesale: it pre-slices logical lines before the final visual component and therefore cannot compute exact omitted visual rows. +- The project currently has no domain glossary or ADR that supersedes the terminology in the pure-display rendering specification. diff --git a/docs/research/upstream-feedback-survivors-2026-07-24.md b/docs/research/upstream-feedback-survivors-2026-07-24.md new file mode 100644 index 0000000..ed6472f --- /dev/null +++ b/docs/research/upstream-feedback-survivors-2026-07-24.md @@ -0,0 +1,196 @@ +# 上游 `MasuRii/pi-tool-display` 反馈在当前重构后的残留审计 + +- **审计对象**:上游仓库 [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display) 的全部 Issue、PR、Issue/PR 评论、PR review 与 inline review comment。 +- **原始审计基线**:`main@4be77657339311093ef07b647d52004221e75416`,2026-07-24。 +- **实施复核日期**:2026-07-25。 + +## 实施复核结论 + +| 上游反馈 | 最终状态 | 验证结果 | +| --- | --- | --- | +| #36 超长单行绕过预览限制 | **已解决** | 非 Diff 完整正文在最终组件按视觉行预算;Diff 按逻辑 Diff 行预算并报告省略视觉 Diff 行 | +| #14/#19 Bash 滚动跳底/闪烁 | **已解决** | 删除 spinner、elapsed tick、`setInterval` 和历史行定时 `invalidate()`;真实 runtime 断言无 animated frame | +| #20/PR #31 Pi 版本兼容 | **已解决** | peer 与 Host gate 支持所有稳定版 `>=0.81.1`;矩阵以 `0.81.1`、`0.82.0` 和 development 作为代表性验证点 | +| PR #27 project-local config | **已解决(只读 overlay)** | 仅 trusted project 读取;显式 global mutation 不持久化 overlay;嵌套 custom 字段保留 siblings | + +最终验证结果:664/664 测试通过;development、Pi 0.81.1、Pi 0.82.0 的真实 runtime contract 全部通过。扩展禁用、session transition 与 reload 使用 owned disposer 恢复或重装 native display seam。验证矩阵是抽样资格验证,不会把支持范围收窄到这两个稳定版本。 + +## 原始审计发现(历史基线) + +> 以下“当前证据”和“仍存在”判断冻结于原始审计基线 `4be7765`,不代表实施后的当前 HEAD;当前结论以文首“实施复核结论”为准。 + +### P1 — 预览预算按逻辑行计数,超长单行仍能填满 viewport(#36) + +**上游证据** + +- Issue body 明确指出 `previewLines`、`bashCollapsedLines`、`expandedPreviewMaxLines` 按换行符计数;10,000 字符且无换行的单行会被视为 1 行,但终端折行后可占数百个 visual rows:[Issue #36](https://github.com/MasuRii/pi-tool-display/issues/36)。 + +**当前证据** + +- `buildPreviewText()` 先按 `splitLines()` 得到逻辑行,并由 `previewLines()` 截取;`renderPreviewText()` 和 `renderBashPreviewWithHints()` 仍将这个逻辑行预算传给 `Text`:[current `tool-overrides.ts` lines 118–140](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/tool-overrides.ts#L118-L140)、[lines 454–477](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/tool-overrides.ts#L454-L477)、[lines 569–586](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/tool-overrides.ts#L569-L586)。 +- 只有 Bash `opencode` collapsed path 使用 `VisualLinePreviewComponent`;Bash `preview`、read/search/MCP/custom preview 与 expanded preview 仍走逻辑行预算:[current `tool-overrides.ts` lines 587–708](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/tool-overrides.ts#L587-L708)。 +- 当前测试覆盖了普通长命令和多行 visual preview,但没有覆盖 10,000 字符无换行的 read/search/MCP/custom 或 Bash preview/expanded case:[`tests/bash-display.test.ts` lines 113–166](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/tests/bash-display.test.ts#L113-L166)、[`tests/pure-bash-display.test.ts` lines 1–40](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/tests/pure-bash-display.test.ts#L1-L40)。 +- 当前 HEAD 的 runtime probe 已确认该缺口:单个 400 字符、无换行输出,`previewLines: 1`、render width 40 时,read、search、custom/MCP result renderer 各产生 10 个 visual rows;只有 Bash `opencode` collapsed 路径已用 `VisualLinePreviewComponent` 处理该维度。 + +**判断**:**仍存在,确认度高**。不仅是代码推导;当前 runtime probe 已直接确认 read/search/custom/MCP 的 visual-row 预算缺失。具体“填满 viewport”的规模取决于输出长度和终端宽度。 + +**建议** + +- 复用并泛化现有 `VisualLinePreviewComponent`,覆盖 read、search、custom/MCP 及 Bash preview/expanded;不要实现第二套折行/宽度算法。 +- 在 renderer 的最终组件边界按终端宽度计算 visual-row budget,并复用 Pi TUI 的 ANSI/Unicode/CJK/emoji 宽度能力。 +- 增加一个最小回归矩阵:宽度 40、400 字符单行、`previewLines: 1`,以及宽度 20、10,000 字符单行的 collapsed/expanded case;断言 rendered rows 有上限、omission hint 只出现一次、原始 tool result/session bytes 不变。 + +### P1 — Bash spinner 仍用历史行定时器触发 `invalidate()`(#14、#19) + +**上游证据** + +- #14 报告长 Bash 输出期间用户滚动时终端反复跳回底部:[Issue #14](https://github.com/MasuRii/pi-tool-display/issues/14)。 +- #19 给出更具体的机制:spinner 位于 message history,每个 timer tick 改变 off-screen 行并调用 `context.invalidate()`,可能造成全屏 repaint/flicker,且多个并行 Bash 会叠加定时器:[Issue #19](https://github.com/MasuRii/pi-tool-display/issues/19)。Issue 中建议使用 Pi 的 working-indicator slot 或直接删除 live tick。 + +**当前证据** + +- 当前实现仍保留每个 tool-call 的 spinner state、`setInterval()` 与 `context.invalidate()`:[current `bash-display.ts` lines 23–40](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/bash-display.ts#L23-L40)、[lines 156–180](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/bash-display.ts#L156-L180)、[lines 234–249](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/bash-display.ts#L234-L249)。 +- 当前代码把间隔从上游报告中的 80ms 调为 200ms,并增加了 session cleanup;这减少了压力和泄漏风险,但没有移除“历史行变更 → 全局 invalidate”这一根因。 +- 现有测试主要验证 timer 数量、完成/cleanup 后清理与 reload 行为,不是滚动位置或实际 flicker:[current `tests/reload-behavior.test.ts` lines 600–660](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/tests/reload-behavior.test.ts#L600-L660)、[current `tests/real-runtime-contract.test.ts` lines 377–398](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/tests/real-runtime-contract.test.ts#L377-L398)。 + +**判断**:**机制仍存在,确认度高;屏幕症状确认度中等**。当前代码足以确认原报告的高风险重绘路径仍未消失,但本次没有真实交互滚动复现,不能声称每个 Pi 版本必然出现跳底/闪烁。 + +**建议** + +- 最小方案是删除行内旋转与 elapsed live tick,依赖 Pi 已有的全局 working indicator,只在真实工具更新和结束时刷新;不要让历史 ToolExecution 行每 200ms 改写自身。Pi 官方如需定制也提供 [`ctx.ui.setWorkingIndicator()`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/working-indicator.ts)。 +- 增加真实 TUI contract:长 Bash 输出、用户滚动到历史位置、一个/多个并行 Bash,断言 viewport anchor 不变、timer 无残留。0.82.0 contract 虽已通过,但未覆盖交互滚动症状,不能替代该验证。 + +### P2 — peer dependency 对历史反馈涉及的 Pi 版本仍有安装缺口(#20、#31) + +**上游证据** + +- #20 的实际安装失败发生在 `@earendil-works/pi-coding-agent@0.74.1`,因为当时 `pi-tool-display@0.4.0` 要求 `^0.75.4`:[Issue #20](https://github.com/MasuRii/pi-tool-display/issues/20)。 +- PR #31 明确把 MCP renderer 兼容问题定位到 Pi `0.80.6+`:[PR #31](https://github.com/MasuRii/pi-tool-display/pull/31)。 +- PR #25 的更新日志也显示依赖曾从 `0.75.4` 逐步升级到 `0.79.8`:[PR #25](https://github.com/MasuRii/pi-tool-display/pull/25)。 + +**当前证据** + +- 当前 package 只接受精确的 `0.74.0`、精确的 `0.80.3`、以及 `>=0.81.1`:[current `package.json` lines 73–77](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/package.json#L73-L77)。因此 `0.74.1` 和 `0.80.6`–`0.80.10` 等版本不满足 npm peer range,即使扩展理论上可以 native fallback,也可能在安装阶段先失败。 +- 兼容矩阵声明了 development、0.81.1、0.82.0 与 0.74.0。本次独立验证中,637/637 non-real-runtime tests、typecheck、build、diff-check 通过,真实 Pi 0.82.0 contract 也通过;development、0.81.1 与 0.74.0 因缺少对应 runtime root 而跳过。已通过的 0.82.0 不能证明被 peer range 排除的历史版本可安装或可运行:[current `tests/real-runtime-contract.test.ts` lines 20–35](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/tests/real-runtime-contract.test.ts#L20-L35)、[`package.json` test script](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/package.json#L28-L31)。 + +**判断**:**仍存在,确认度高(peer range);实际每个中间版本的 runtime 行为确认度中等**。问题已从“错误要求 0.75.x”变成“只允许少数已声明版本”,但 #20 的 `0.74.1` 安装失败形态仍可由当前 range 推导。 + +**建议** + +- 支持所有稳定版 `>=0.81.1`,并在 README 中区分“支持范围”和“代表性验证矩阵”;不要把抽测版本误写为仅支持的精确版本。[README compatibility](https://github.com/liuli195/pi-tool-display/blob/main/README.md#L97-L103)。 + +### P3 — project-local config 尚未实现(PR #27) + +**上游证据** + +- PR #27 提议 `.pi/extensions/pi-tool-display/config.json`,project → global → defaults 的优先级,以及 `/tool-display reset|preset --project|--global`:[PR #27](https://github.com/MasuRii/pi-tool-display/pull/27)。该 PR 没有 actionable review comment,且仍为未合并状态。 + +**当前证据** + +- 当前 config path 在模块加载时固定为 Pi agent 全局目录下的 `extensions/pi-tool-display/config.json`:[current `src/config-store.ts` lines 25–26](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/config-store.ts#L25-L26)。`loadToolDisplayConfig()`/`saveToolDisplayConfig()` 也只接收单个 config file,未实现 project/global merge:[current `src/config-store.ts` lines 231–277](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/src/config-store.ts#L231-L277)。 +- README 只记录 global config path,并在 troubleshooting 中要求检查 global 文件:[current README lines 145–149](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/README.md#L145-L149)、[lines 343–347](https://github.com/liuli195/pi-tool-display/blob/4be77657339311093ef07b647d52004221e75416/README.md#L343-L347)。 + +**判断**:**仍存在,确认度高**。这是功能缺口/UX limitation,不是当前纯显示重构的必需部分。 + +**安全边界(必须遵守)** + +Pi 官方 `extensions.md` 要求:project-local path 使用 `CONFIG_DIR_NAME` 而不是硬编码 `.pi`,并在读取应只对可信项目生效的配置前调用 `ctx.isProjectTrusted()`:[官方文档 `ctx.cwd` / `CONFIG_DIR_NAME`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md#ctxcwd)、[官方文档 `ctx.isProjectTrusted()`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md#ctxisprojecttrusted)。本机安装的 0.82-ish docs 同样明确记载:`CONFIG_DIR_NAME` 位于 lines 952–963,`ctx.isProjectTrusted()` 位于 lines 966–970。 + +**建议** + +- 若要实现:在 `session_start` 或等价拥有 `ctx` 的生命周期读取 global + `join(ctx.cwd, CONFIG_DIR_NAME, ...)` 的 project config;只有 `ctx.isProjectTrusted() === true` 才读取/合并项目文件。 +- 不要继续在 extension factory 通过 `process.cwd()` 直接读仓库配置;这样既没有当前 session trust context,也会让 reload/session switch 使用 stale cwd。 +- 保存操作也应有明确 scope:默认保持 global;project save/reset 必须是显式命令,且再次检查 trust,不要让 `/tool-display` 无意间写入不可信仓库。 +- 先做最小 project read-only precedence;scope flags 与 project write 可以后置。项目配置本身不应改变工具、执行、context 或 session 内容。 + +## 证据与方法说明 + +### 数据范围 + +截至本报告时间,上游共审计 **37 个编号对象**:Issue #3/#5/#8/#14/#15/#17/#19/#20/#21/#23/#26/#29/#30/#33/#35/#36,以及 PR #1/#2/#4/#6/#7/#9/#10/#11/#12/#13/#16/#18/#22/#24/#25/#27/#28/#31/#32/#34/#37。包括已关闭、未合并、已合并、开放项目,不按 open/closed 过滤。 + +### 精确查询/API 方法 + +使用 GitHub CLI `gh 2.93.0` 调用 GitHub REST API,仓库均显式指定为 `MasuRii/pi-tool-display`: + +```text +gh api 'repos/MasuRii/pi-tool-display/issues?state=all&per_page=100' --paginate +gh api 'repos/MasuRii/pi-tool-display/pulls?state=all&per_page=100' --paginate +``` + +对每个 `n = 1..37`: + +```text +gh api 'repos/MasuRii/pi-tool-display/issues/{n}' +gh api 'repos/MasuRii/pi-tool-display/issues/{n}/comments?per_page=100' +``` + +对每个 PR: + +```text +gh api 'repos/MasuRii/pi-tool-display/pulls/{n}/reviews?per_page=100' +gh api 'repos/MasuRii/pi-tool-display/pulls/{n}/comments?per_page=100' +gh api 'repos/MasuRii/pi-tool-display/pulls/{n}/files?per_page=100' +``` + +此外阅读了当前仓库的 `AGENTS.md`、`docs/agents/issue-tracker.md`、`docs/agents/domain.md`、`README.md`、`CHANGELOG.md`、`docs/spec/pure-display-tool-rendering.md`、`docs/research/pi-history-rendering-root-cause.md`,以及当前 commit 的 source/tests。仓库根目录没有 `CONTEXT.md`,`docs/adr/` 也不存在;没有因此创建文件或改变代码。 + +PR review 结果:绝大多数 PR 没有 review body 或 inline review comment;PR #10 有一个空 body 的 approval review:[review permalink](https://github.com/MasuRii/pi-tool-display/pull/10#pullrequestreview-4136230745)。有实质内容的反馈主要位于 issue comments/PR conversation,均在下方矩阵保留入口。 + +## 附录 A:完整 upstream coverage matrix + +状态含义: + +- **仍存在**:进入主表。 +- **已由重构解决**:当前源码/测试显示原根因已移除;若依赖真实 runtime,单独标注验证限制。 +- **obsolete/out of scope**:原建议已放弃、依赖更新已无意义,或与当前纯显示边界不冲突。 +- **cannot verify**:当前静态证据不足以替代真实运行时复现;不把 skipped runtime 当作已解决。 + + +| 编号 | 类型 / 主题 | 讨论与 review 入口 | 当前状态 | 当前判断依据 | +| --- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | +| #1 | Dependabot `file-type` 安全更新 | [PR #1](https://github.com/MasuRii/pi-tool-display/pull/1)、[bot comment](https://github.com/MasuRii/pi-tool-display/pull/1#issuecomment-4047124118) | obsolete/out of scope | bot 已说明依赖不再可更新;无产品反馈。 | +| #2 | 兼容 fork 的重复 thinking label | [PR #2](https://github.com/MasuRii/pi-tool-display/pull/2) | 已由重构解决 / obsolete | thinking-label 功能已移除;当前 `src/index.ts` 不注册相关 message/context handlers。 | +| #3 | inline diff emphasis 被遮蔽 | [Issue #3](https://github.com/MasuRii/pi-tool-display/issues/3)、[fix comment](https://github.com/MasuRii/pi-tool-display/issues/3#issuecomment-4049147085) | 已由重构解决 | 上游已说明 commit `72ad294` 修复;当前保留 ANSI/diff 回归测试。 | +| #4 | Dependabot `undici` 安全更新 | [PR #4](https://github.com/MasuRii/pi-tool-display/pull/4)、[bot comment](https://github.com/MasuRii/pi-tool-display/pull/4#issuecomment-4109920044) | obsolete/out of scope | bot 已说明依赖不再可更新。 | +| #5 | 增加 hide/collapse/truncate 关键词 | [Issue #5](https://github.com/MasuRii/pi-tool-display/issues/5)、[实现 comment](https://github.com/MasuRii/pi-tool-display/issues/5#issuecomment-4101500452) | 已由重构解决 | 当前 `package.json` keywords 含 `hide/collapse/truncate` 等。 | +| #6 | 支持 `PI_CODING_AGENT_DIR` | [PR #6](https://github.com/MasuRii/pi-tool-display/pull/6) | 已由重构解决 | 当前 `src/agent-dir.ts`/config path 使用该环境变量解析。 | +| #7 | Dependabot `brace-expansion` | [PR #7](https://github.com/MasuRii/pi-tool-display/pull/7) | obsolete/out of scope | 无产品反馈。 | +| #8 | RGB 颜色分量 `49` 误判背景 reset | [Issue #8](https://github.com/MasuRii/pi-tool-display/issues/8)、[comment](https://github.com/MasuRii/pi-tool-display/issues/8#issuecomment-4275690702) | 已由重构解决 | 当前 ANSI 测试覆盖 foreground RGB preservation。 | +| #9 | Dependabot `basic-ftp` | [PR #9](https://github.com/MasuRii/pi-tool-display/pull/9)、[bot comment](https://github.com/MasuRii/pi-tool-display/pull/9#issuecomment-4298720465) | obsolete/out of scope | bot 已说明依赖不再可更新。 | +| #10 | native user message box spacing/OSC | [PR #10](https://github.com/MasuRii/pi-tool-display/pull/10)、[approval](https://github.com/MasuRii/pi-tool-display/pull/10#pullrequestreview-4136230745) | 已由重构解决 | 当前 native user-message pipeline 与 OSC/嵌套 Markdown 测试仍在。 | +| #11 | Dependabot 两依赖更新 | [PR #11](https://github.com/MasuRii/pi-tool-display/pull/11)、[bot comment](https://github.com/MasuRii/pi-tool-display/pull/11#issuecomment-4314387651) | obsolete/out of scope | 无产品反馈;bot 已关闭。 | +| #12 | Ctrl+O hint 与 expanded preview | [PR #12](https://github.com/MasuRii/pi-tool-display/pull/12) | 已由重构解决 | 当前 README、Bash/search/read renderer 与 tests 保留展开/折叠行为。 | +| #13 | Shiki/multi-edit/diff visual upgrades | [PR #13](https://github.com/MasuRii/pi-tool-display/pull/13)、[discussion](https://github.com/MasuRii/pi-tool-display/pull/13#issuecomment-4364352439) | obsolete/out of scope | 作者明确决定放弃;当前 spec 也不要求该大改版。 | +| #14 | 长 Bash 输出时手动滚动跳底 | [Issue #14](https://github.com/MasuRii/pi-tool-display/issues/14) | **仍存在** | 与 #19 合并为 spinner/invalidate 残留;见主表。 | +| #15 | MCP 注册时序、任意 top-level tool 太吵 | [Issue #15](https://github.com/MasuRii/pi-tool-display/issues/15)、[custom-tools comment](https://github.com/MasuRii/pi-tool-display/issues/15#issuecomment-4610096529)、[late-tool diagnosis](https://github.com/MasuRii/pi-tool-display/issues/15#issuecomment-4727855461) | 已由重构解决(runtime caveat) | 当前有 `customToolOverrides` 与 disposable Renderer Adapter;不再靠 MCP heuristic。0.82.0 contract 已通过,但未覆盖全部 MCP producer/load-order 组合。 | +| #16 | Dependabot `fast-xml-builder` | [PR #16](https://github.com/MasuRii/pi-tool-display/pull/16) | obsolete/out of scope | 无产品反馈。 | +| #17 | 与 `pi-rtk` 的 `bash` ownership 冲突 | [Issue #17](https://github.com/MasuRii/pi-tool-display/issues/17)、[workaround comment](https://github.com/MasuRii/pi-tool-display/issues/17#issuecomment-4412539767) | 已由重构解决 | 当前 display seam 不注册/替换 executable tool。 | +| #18 | MCP override 静默失效 | [PR #18](https://github.com/MasuRii/pi-tool-display/pull/18) | 已由重构解决(runtime caveat) | 当前 Host Adapter + explicit adapter/catalog 取代 clone/registration 路径;0.82.0 contract 已通过,其他声明版本尚未运行。 | +| #19 | spinner 定时器造成 pi-tui flicker | [Issue #19](https://github.com/MasuRii/pi-tool-display/issues/19) | **仍存在** | 当前 `bash-display.ts:236–248` 仍为 `setInterval` + `invalidate()`;见主表。 | +| #20 | latest Pi npm `ERESOLVE` | [Issue #20](https://github.com/MasuRii/pi-tool-display/issues/20) | **仍存在** | 当前 peer range 仍排除 `0.74.1`;见主表。 | +| #21 | Bash 丢失 `shellPath/commandPrefix` | [Issue #21](https://github.com/MasuRii/pi-tool-display/issues/21)、[fix PR #22](https://github.com/MasuRii/pi-tool-display/pull/22) | 已由重构解决 | 更根本地,当前扩展不重建 Bash executable、不包装 execution;shell settings 不会因显示而丢失。 | +| #22 | Bash settings passthrough fix | [PR #22](https://github.com/MasuRii/pi-tool-display/pull/22) | 已由重构解决 | 同 #21;执行边界已移除。 | +| #23 | 小 tmux pane 的 large diff 闪烁 | [Issue #23](https://github.com/MasuRii/pi-tool-display/issues/23)、[closure comment](https://github.com/MasuRii/pi-tool-display/issues/23#issuecomment-4877759144) | 已由重构解决(runtime caveat) | 当前 expanded diff 有 `expandedPreviewMaxLines` 与 omission hint;具体各真实 tmux 终端仍需 runtime contract。 | +| #24 | Dependabot `esbuild` | [PR #24](https://github.com/MasuRii/pi-tool-display/pull/24) | obsolete/out of scope | 依赖维护项,无上游产品反馈。 | +| #25 | Dependabot coding-agent/protobufjs/ws | [PR #25](https://github.com/MasuRii/pi-tool-display/pull/25)、[bot comment](https://github.com/MasuRii/pi-tool-display/pull/25#issuecomment-4872253177) | obsolete/out of scope | 依赖维护项;当前兼容性另见 #20/#31。 | +| #26 | `find/ls` off-by-default 被激活 | [Issue #26](https://github.com/MasuRii/pi-tool-display/issues/26)、[closure comment](https://github.com/MasuRii/pi-tool-display/issues/26#issuecomment-4878351402) | 已由重构解决 | 当前无 built-in re-registration/activation;display selection 与 ownership 分离。 | +| #27 | project-level config 与 scope flags | [PR #27](https://github.com/MasuRii/pi-tool-display/pull/27) | **仍存在** | 当前只有 global config path;见主表。 | +| #28 | load-time renderer 以修复 restored history | [PR #28](https://github.com/MasuRii/pi-tool-display/pull/28) | cannot verify | 当前 Host Adapter/row seam 设计上替代 load-time registration,但历史 rows 的真实 0.81.x 首帧 contract 被跳过,不能把 mock tests 当成证明。 | +| #29 | late owner 导致 built-in hard crash | [Issue #29](https://github.com/MasuRii/pi-tool-display/issues/29)、[closure comment](https://github.com/MasuRii/pi-tool-display/issues/29#issuecomment-4878351407) | 已由重构解决 | 当前显示层不抢 executable ownership;不会再因 renderer 配置注册同名 tool。 | +| #30 | thinking finalization `theme.fg` 错误 | [Issue #30](https://github.com/MasuRii/pi-tool-display/issues/30) | obsolete/out of scope | thinking-label finalization 已删除;不存在该调用路径。 | +| #31 | Pi 0.80.6+ MCP prototype patch | [PR #31](https://github.com/MasuRii/pi-tool-display/pull/31) | 已由重构解决(runtime caveat) | 当前 producer adapter + Host Adapter 是纯显示路径;但 package 对 `0.80.6` 的 peer 安装缺口仍是主表兼容项。 | +| #32 | later extension tool 在注册前 decoration | [PR #32](https://github.com/MasuRii/pi-tool-display/pull/32) | 已由重构解决 | 当前 consumer API 注册 display intent/adapter,不修改 frozen ToolDefinition;见 README API 说明。 | +| #33 | thinking label 修改内容、影响其他 extension | [Issue #33](https://github.com/MasuRii/pi-tool-display/issues/33)、关联报告入口见 issue body | 已由重构解决 | 当前已移除 thinking label/message/context mutation;纯显示 spec 明确禁止该行为。 | +| #34 | `pi-patty-bg-tasks` startup bash race | [PR #34](https://github.com/MasuRii/pi-tool-display/pull/34)、[tracking comment](https://github.com/MasuRii/pi-tool-display/pull/34#issuecomment-5050977299) | 已由重构解决 | no registration/no ownership mutation;真实双包启动仍属于未提供 runtime root 的 contract caveat。 | +| #35 | 与 `pi-patty-bg-tasks` 的 bash 冲突 | [Issue #35](https://github.com/MasuRii/pi-tool-display/issues/35) | 已由重构解决(runtime caveat) | 当前不调用 registerTool 取得显示控制;源代码已去除根因。 | +| #36 | 超长单行绕过 preview limit | [Issue #36](https://github.com/MasuRii/pi-tool-display/issues/36) | **仍存在** | 当前 preview/search/MCP/custom 仍按逻辑行 slice;见主表。 | +| #37 | upstream ownership fix(明确 intended for fork) | [PR #37](https://github.com/MasuRii/pi-tool-display/pull/37)、[closure comment](https://github.com/MasuRii/pi-tool-display/pull/37#issuecomment-5059417608) | obsolete/out of scope / 已由 fork 重构吸收 | 上游 comment 明确关闭原因是改动 intended for fork;当前 liuli fork 已吸收纯显示方向。 | + + +## 附录 B:验证限制 + +- 本次没有修改产品代码、配置或 GitHub issue;只新增本审计报告。 +- 当前 HEAD 验证:637/637 non-real-runtime tests passed;typecheck/build/`git diff --check` passed;真实 Pi 0.82.0 contract passed。development、Pi 0.81.1 与 minimum-supported runtime 因缺少对应 `PI_RUNTIME_*_ROOT` 而 skipped。 +- 因此:0.82.0 生命周期 contract 有真实运行时证据;仍不能据此证明 0.74.0/0.81.1、交互滚动 viewport、真实 tmux 或所有第三方包共存组合已解决。 +- 报告中的“已由重构解决(runtime caveat)”表示当前源码已移除上游报告的旧根因,并非声称所有声明版本和组合均已验收。 diff --git a/package-lock.json b/package-lock.json index eb64680..f280cd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,15 @@ { - "name": "pi-tool-display", - "version": "0.6.2", + "name": "@plus/pi-tool-display", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "pi-tool-display", - "version": "0.6.2", - "hasInstallScript": true, + "name": "@plus/pi-tool-display", + "version": "0.1.0", "license": "MIT", "devDependencies": { - "@earendil-works/pi-coding-agent": "^0.80.3", + "@earendil-works/pi-coding-agent": "^0.81.1", "pi-mcp-adapter": "^2.11.0", "tsx": "4.22.4", "typescript": "6.0.3" @@ -19,8 +18,8 @@ "node": ">=20" }, "peerDependencies": { - "@earendil-works/pi-coding-agent": "0.74.0 || 0.80.3 || >=0.81.1", - "@earendil-works/pi-tui": "0.74.0 || 0.80.3 || >=0.81.1" + "@earendil-works/pi-coding-agent": "*", + "@earendil-works/pi-tui": "*" } }, "node_modules/@anthropic-ai/sdk": { @@ -440,16 +439,16 @@ } }, "node_modules/@earendil-works/pi-coding-agent": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.80.3.tgz", - "integrity": "sha512-TIggw9gCXpA+Ph7OjdTA7ka2NPwTVuPmy39KDSyUzaKq8VvHfMGR7vtRz4JB7Um/RMRblmzhu4p9tUCk6MTgGA==", + "version": "0.81.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.81.1.tgz", + "integrity": "sha512-r6ovAsZOgAqbC/aU6s+/dPnv/sGZBuWyZNvi3pXjpbuX5wvp3XvGkQI7/VLvX2o9XpmpFaPUxKNym1WfkN/P8A==", "dev": true, "hasShrinkwrap": true, "license": "MIT", "dependencies": { - "@earendil-works/pi-agent-core": "^0.80.3", - "@earendil-works/pi-ai": "^0.80.3", - "@earendil-works/pi-tui": "^0.80.3", + "@earendil-works/pi-agent-core": "^0.81.1", + "@earendil-works/pi-ai": "^0.81.1", + "@earendil-works/pi-tui": "^0.81.1", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", @@ -939,12 +938,12 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.3.tgz", + "version": "0.81.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.81.1.tgz", "dev": true, "license": "MIT", "dependencies": { - "@earendil-works/pi-ai": "^0.80.3", + "@earendil-works/pi-ai": "^0.81.1", "ignore": "7.0.5", "typebox": "1.1.38", "yaml": "2.9.0" @@ -954,8 +953,8 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.3.tgz", + "version": "0.81.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.81.1.tgz", "dev": true, "license": "MIT", "dependencies": { @@ -972,15 +971,15 @@ "typebox": "1.1.38" }, "bin": { - "pi-ai": "./dist/cli.js" + "pi-ai": "dist/cli.js" }, "engines": { "node": ">=22.19.0" } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.3.tgz", + "version": "0.81.1", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.81.1.tgz", "dev": true, "license": "MIT", "dependencies": { @@ -1531,9 +1530,9 @@ "license": "MIT" }, "node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 2911a0c..fb3e5b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "pi-tool-display", - "version": "0.6.2", + "name": "@plus/pi-tool-display", + "version": "0.1.0", "description": "Pure TUI display wrapper for compact Pi tool rows and trustworthy tool-provided diffs, without changing tool execution or model/session data.", "type": "module", "main": "./index.ts", @@ -11,24 +11,14 @@ "default": "./tool-display-api-consumer.js" } }, - "files": [ - "index.ts", - "tool-display-api-consumer.js", - "tool-display-api-consumer.d.ts", - "src", - "config/config.example.json", - "README.md", - "CHANGELOG.md", - "LICENSE" - ], "scripts": { "build": "tsc -p tsconfig.json", "lint": "npm run typecheck", "test": "tsx --test tests/*.test.ts", "test:contract:local": "tsx --test tests/real-runtime-contract.test.ts", + "test:contract:required": "tsx --test tests/real-runtime-contract.test.ts", "check": "npm run lint && npm run test", - "typecheck": "tsc -p tsconfig.json", - "postinstall": "node -e \"const fs=require('fs'),cp=require('child_process'),p=require('path');const cwd=process.cwd();const normalized=cwd.split(p.sep).join('/');if(!normalized.includes('/.pi/agent/extensions/'))process.exit(0);const s=p.resolve(cwd,'../../scripts/patch-vulnerable-deps.mjs');if(!fs.existsSync(s))process.exit(0);const r=cp.spawnSync(process.execPath,[s,'--target',cwd,'--quiet'],{stdio:'inherit'});process.exit(r.status||0)\"" + "typecheck": "tsc -p tsconfig.json" }, "keywords": [ "pi-package", @@ -62,17 +52,14 @@ "engines": { "node": ">=20" }, - "publishConfig": { - "access": "public" - }, "pi": { "extensions": [ "./index.ts" ] }, "peerDependencies": { - "@earendil-works/pi-coding-agent": "0.74.0 || 0.80.3 || >=0.81.1", - "@earendil-works/pi-tui": "0.74.0 || 0.80.3 || >=0.81.1" + "@earendil-works/pi-coding-agent": "*", + "@earendil-works/pi-tui": "*" }, "overrides": { "file-type": ">=21.3.1", @@ -80,7 +67,7 @@ "ws": "8.21.0" }, "devDependencies": { - "@earendil-works/pi-coding-agent": "^0.80.3", + "@earendil-works/pi-coding-agent": "^0.81.1", "pi-mcp-adapter": "^2.11.0", "tsx": "4.22.4", "typescript": "6.0.3" diff --git a/src/bash-display.ts b/src/bash-display.ts index fe9d8d9..383e658 100644 --- a/src/bash-display.ts +++ b/src/bash-display.ts @@ -1,11 +1,5 @@ -import { Text, truncateToWidth, type Component } from "@earendil-works/pi-tui"; +import { Text, truncateToWidth, visibleWidth, type Component } from "@earendil-works/pi-tui"; import { DEFAULT_TOOL_DISPLAY_CONFIG, type ToolDisplayConfig } from "./types.js"; -import { registerSessionCleanup } from "./disposable.js"; - -const BASH_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const; -const BASH_SPINNER_INTERVAL_MS = 200; -const BASH_SPINNER_STATE_KEY = "__piToolDisplayBashSpinner"; -const BASH_SPINNER_TOOL_CALL_ID_KEY = "__piToolDisplayBashSpinnerToolCallId"; interface BashCallArgs { command?: string; @@ -19,120 +13,11 @@ interface BashCallRenderTheme { bold(text: string): string; } -interface BashSpinnerState { - frameIndex: number; - startedAt?: number; - timer?: ReturnType; - unregisterCleanup?: () => void; - carriers?: Set; -} - -interface BashSpinnerStateCarrier { - [BASH_SPINNER_STATE_KEY]?: BashSpinnerState; - [BASH_SPINNER_TOOL_CALL_ID_KEY]?: string; -} - interface BashCallRenderContextLike { executionStarted: boolean; isPartial: boolean; expanded?: boolean; - invalidate?: () => void; lastComponent?: unknown; - state?: unknown; - toolCallId?: string; -} - -const spinnerStatesByToolCallId = new Map(); -let nextSyntheticToolCallId = 0; - -function toStateCarrier(value: unknown): BashSpinnerStateCarrier | undefined { - if (!value || typeof value !== "object") { - return undefined; - } - return value as BashSpinnerStateCarrier; -} - -function getSyntheticToolCallId(carrier: BashSpinnerStateCarrier | undefined): string | undefined { - if (!carrier) { - return undefined; - } - - if (!carrier[BASH_SPINNER_TOOL_CALL_ID_KEY]) { - carrier[BASH_SPINNER_TOOL_CALL_ID_KEY] = `state:${++nextSyntheticToolCallId}`; - } - return carrier[BASH_SPINNER_TOOL_CALL_ID_KEY]; -} - -function getToolCallId(context: BashCallRenderContextLike): string | undefined { - if (typeof context.toolCallId === "string" && context.toolCallId.trim().length > 0) { - return context.toolCallId; - } - return getSyntheticToolCallId(toStateCarrier(context.state)); -} - -function getOrCreateSpinnerState( - toolCallId: string | undefined, - carrier: BashSpinnerStateCarrier | undefined, -): BashSpinnerState | undefined { - if (!toolCallId) { - return undefined; - } - - let state = spinnerStatesByToolCallId.get(toolCallId); - if (!state) { - state = { frameIndex: 0 }; - spinnerStatesByToolCallId.set(toolCallId, state); - const created = state; - created.unregisterCleanup = registerSessionCleanup(() => { - created.unregisterCleanup = undefined; - stopSpinner(toolCallId, created); - }); - } - if (carrier) { - state.carriers ??= new Set(); - state.carriers.add(carrier); - carrier[BASH_SPINNER_STATE_KEY] = state; - } - return state; -} - -function stopSpinner( - toolCallId: string | undefined, - state: BashSpinnerState | undefined, - carrier?: BashSpinnerStateCarrier, -): void { - if (!state) return; - - state.unregisterCleanup?.(); - state.unregisterCleanup = undefined; - if (state.timer) clearInterval(state.timer); - state.timer = undefined; - state.frameIndex = 0; - state.startedAt = undefined; - if (toolCallId) spinnerStatesByToolCallId.delete(toolCallId); - if (carrier) state.carriers?.add(carrier); - for (const stateCarrier of state.carriers ?? []) { - if (stateCarrier[BASH_SPINNER_STATE_KEY] === state) delete stateCarrier[BASH_SPINNER_STATE_KEY]; - if (stateCarrier[BASH_SPINNER_TOOL_CALL_ID_KEY] === toolCallId) delete stateCarrier[BASH_SPINNER_TOOL_CALL_ID_KEY]; - } - state.carriers?.clear(); -} - -function formatElapsed(elapsedMs: number): string { - const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1000)); - if (totalSeconds < 60) { - return `${totalSeconds}s`; - } - - const totalMinutes = Math.floor(totalSeconds / 60); - const seconds = totalSeconds % 60; - if (totalMinutes < 60) { - return `${totalMinutes}m ${seconds}s`; - } - - const hours = Math.floor(totalMinutes / 60); - const minutes = totalMinutes % 60; - return `${hours}h ${minutes}m`; } function isDefaultShellPath(shellPath: string): boolean { @@ -160,6 +45,7 @@ export class VisualLinePreviewComponent implements Component { private previewLines: number, private expanded: boolean, private theme: BashCallRenderTheme, + private expandedBypass: boolean = false, ) {} setDisplay(text: string, previewLines: number, expanded: boolean): void { @@ -170,10 +56,17 @@ export class VisualLinePreviewComponent implements Component { render(width: number): string[] { const lines = this.text.render(width); - if (this.expanded || lines.length <= this.previewLines) return lines; - - const hint = this.theme.fg("muted", `... (${lines.length - this.previewLines} more visual lines • Ctrl+O to expand)`); - return [...lines.slice(0, this.previewLines), truncateToWidth(hint, width)]; + if (this.expanded && this.expandedBypass) return lines; + const budget = Math.max(0, Math.floor(this.previewLines)); + if (lines.length <= budget) return lines; + + const omitted = lines.length - budget; + const unit = omitted === 1 ? "line" : "lines"; + const candidates = this.expanded + ? [`… (${omitted} more visual ${unit} • display capped)`, `… (${omitted} more visual ${unit})`, `… (+${omitted})`, "…"] + : [`… (${omitted} more visual ${unit} • Ctrl+O to expand)`, `… (${omitted} more visual ${unit})`, `… (+${omitted})`, "…"]; + const hint = candidates.find((candidate) => visibleWidth(candidate) <= width) ?? truncateToWidth("…", width, ""); + return [...lines.slice(0, budget), this.theme.fg("muted", hint)]; } invalidate(): void { @@ -186,8 +79,6 @@ class BashCallComponent extends VisualLinePreviewComponent {} function buildBashCallText( args: BashCallArgs, theme: BashCallRenderTheme, - spinnerFrame?: string, - elapsedMs?: number, ): string { const commandDisplay = buildCommandDisplay(args); const shellSuffix = @@ -199,13 +90,8 @@ function buildBashCallText( const timeoutSuffix = args.timeout ? theme.fg("muted", ` (timeout ${args.timeout}s)`) : ""; - const spinnerPrefix = spinnerFrame ? `${theme.fg("warning", `${spinnerFrame} `)}` : ""; - const elapsedSuffix = - spinnerFrame && elapsedMs !== undefined - ? theme.fg("muted", ` · ${formatElapsed(elapsedMs)}`) - : ""; - return `${spinnerPrefix}${theme.fg("toolTitle", theme.bold("$"))} ${theme.fg("accent", commandDisplay)}${shellSuffix}${timeoutSuffix}${elapsedSuffix}`; + return `${theme.fg("toolTitle", theme.bold("$"))} ${theme.fg("accent", commandDisplay)}${shellSuffix}${timeoutSuffix}`; } export function renderBashCall( @@ -218,42 +104,7 @@ export function renderBashCall( const expanded = context.expanded === true || config.bashCommandMode === "full"; const text = context.lastComponent instanceof BashCallComponent ? context.lastComponent - : new BashCallComponent(previewLines, expanded, theme); - const setDisplay = (content: string): void => text.setDisplay(content, previewLines, expanded); - const carrier = toStateCarrier(context.state); - const toolCallId = getToolCallId(context); - const spinnerState = getOrCreateSpinnerState(toolCallId, carrier); - const shouldSpin = context.executionStarted && context.isPartial; - - if (!shouldSpin) { - stopSpinner(toolCallId, spinnerState, carrier); - setDisplay(buildBashCallText(args, theme)); - return text; - } - - if (spinnerState) { - spinnerState.startedAt ??= Date.now(); - if (!spinnerState.timer && typeof context.invalidate === "function") { - const timer = setInterval(() => { - spinnerState.frameIndex = (spinnerState.frameIndex + 1) % BASH_SPINNER_FRAMES.length; - setDisplay( - buildBashCallText( - args, - theme, - BASH_SPINNER_FRAMES[spinnerState.frameIndex], - Date.now() - (spinnerState.startedAt ?? Date.now()), - ), - ); - context.invalidate?.(); - }, BASH_SPINNER_INTERVAL_MS); - spinnerState.timer = timer; - } - } - - const spinnerFrame = spinnerState ? BASH_SPINNER_FRAMES[spinnerState.frameIndex] : undefined; - const elapsedMs = spinnerState?.startedAt !== undefined - ? Date.now() - spinnerState.startedAt - : undefined; - setDisplay(buildBashCallText(args, theme, spinnerFrame, elapsedMs)); + : new BashCallComponent(previewLines, expanded, theme, true); + text.setDisplay(buildBashCallText(args, theme), previewLines, expanded); return text; } diff --git a/src/config-command.ts b/src/config-command.ts index 5aede7f..6948a60 100644 --- a/src/config-command.ts +++ b/src/config-command.ts @@ -1,10 +1,16 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import type { ToolDisplayCapabilities } from "./capabilities.js"; -import type { ToolDisplayConfig } from "./types.js"; +import type { ToolDisplayPreset } from "./presets.js"; +import type { ToolDisplayConfig, ToolDisplayConfigPatch } from "./types.js"; + +export type ToolDisplayConfigMutation = + | { type: "patch"; patch: ToolDisplayConfigPatch } + | { type: "preset"; preset: ToolDisplayPreset } + | { type: "reset" }; export interface ToolDisplayConfigController { getConfig(): ToolDisplayConfig; - setConfig(next: ToolDisplayConfig, ctx: ExtensionCommandContext): void; + mutateConfig(mutation: ToolDisplayConfigMutation, ctx: ExtensionCommandContext): void; getCapabilities(): ToolDisplayCapabilities; } diff --git a/src/config-modal.ts b/src/config-modal.ts index d377004..bbc37c4 100644 --- a/src/config-modal.ts +++ b/src/config-modal.ts @@ -1,7 +1,7 @@ import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import type { ToolDisplayCapabilities } from "./capabilities.js"; -import type { ToolDisplayConfigController } from "./config-command.js"; -import { getToolDisplayConfigPath } from "./config-store.js"; +import type { ToolDisplayConfigController, ToolDisplayConfigMutation } from "./config-command.js"; +import { getToolDisplayConfigPath, mergeProjectConfig } from "./config-store.js"; import { detectToolDisplayPreset, getToolDisplayPresetConfig, @@ -391,86 +391,55 @@ export function buildInspectorSettings( return items; } -function applyPreset(preset: ToolDisplayPreset): ToolDisplayConfig { - return getToolDisplayPresetConfig(preset); -} - -export function applySetting(config: ToolDisplayConfig, id: string, value: string): ToolDisplayConfig { +export function createSettingMutation( + config: ToolDisplayConfig, + id: string, + value: string, +): ToolDisplayConfigMutation | undefined { switch (id) { case "preset": { - const parsed = parseToolDisplayPreset(value); - return parsed ? applyPreset(parsed) : config; + const preset = parseToolDisplayPreset(value); + return preset ? { type: "preset", preset } : undefined; } case "enableNativeUserMessageBox": - return { - ...config, - enableNativeUserMessageBox: value === "on", - }; + return { type: "patch", patch: { enableNativeUserMessageBox: value === "on" } }; case "readOutputMode": - return { - ...config, - readOutputMode: value as ToolDisplayConfig["readOutputMode"], - }; + return { type: "patch", patch: { readOutputMode: value as ToolDisplayConfig["readOutputMode"] } }; case "searchOutputMode": - return { - ...config, - searchOutputMode: value as ToolDisplayConfig["searchOutputMode"], - }; + return { type: "patch", patch: { searchOutputMode: value as ToolDisplayConfig["searchOutputMode"] } }; case "previewLines": - return { - ...config, - previewLines: parseNumber(value, config.previewLines), - }; + return { type: "patch", patch: { previewLines: parseNumber(value, config.previewLines) } }; case "bashOutputMode": - return { - ...config, - bashOutputMode: value as ToolDisplayConfig["bashOutputMode"], - }; + return { type: "patch", patch: { bashOutputMode: value as ToolDisplayConfig["bashOutputMode"] } }; case "bashCollapsedLines": - return { - ...config, - bashCollapsedLines: parseNumber(value, config.bashCollapsedLines), - }; + return { type: "patch", patch: { bashCollapsedLines: parseNumber(value, config.bashCollapsedLines) } }; case "bashCommandMode": - return { - ...config, - bashCommandMode: value as ToolDisplayConfig["bashCommandMode"], - }; + return { type: "patch", patch: { bashCommandMode: value as ToolDisplayConfig["bashCommandMode"] } }; case "bashCommandPreviewLines": - return { - ...config, - bashCommandPreviewLines: parseNumber(value, config.bashCommandPreviewLines), - }; + return { type: "patch", patch: { bashCommandPreviewLines: parseNumber(value, config.bashCommandPreviewLines) } }; case "bashErrorOutputMode": - return { - ...config, - bashErrorOutputMode: value as ToolDisplayConfig["bashErrorOutputMode"], - }; + return { type: "patch", patch: { bashErrorOutputMode: value as ToolDisplayConfig["bashErrorOutputMode"] } }; case "bashErrorPreviewLines": - return { - ...config, - bashErrorPreviewLines: parseNumber(value, config.bashErrorPreviewLines), - }; + return { type: "patch", patch: { bashErrorPreviewLines: parseNumber(value, config.bashErrorPreviewLines) } }; case "diffViewMode": - return { - ...config, - diffViewMode: value as ToolDisplayConfig["diffViewMode"], - }; + return { type: "patch", patch: { diffViewMode: value as ToolDisplayConfig["diffViewMode"] } }; case "diffIndicatorMode": - return { - ...config, - diffIndicatorMode: value as ToolDisplayConfig["diffIndicatorMode"], - }; + return { type: "patch", patch: { diffIndicatorMode: value as ToolDisplayConfig["diffIndicatorMode"] } }; case "showRtkCompactionHints": - return { - ...config, - showRtkCompactionHints: value === "on", - }; + return { type: "patch", patch: { showRtkCompactionHints: value === "on" } }; default: - return config; + return undefined; } } +export function applySetting(config: ToolDisplayConfig, id: string, value: string): ToolDisplayConfig { + const mutation = createSettingMutation(config, id, value); + if (!mutation || mutation.type === "reset") return config; + return mutation.type === "preset" + ? getToolDisplayPresetConfig(mutation.preset) + : mergeProjectConfig(config, mutation.patch); +} + function resolveResponsiveOverlayOptions(): ModalOverlayOptions { const terminalWidth = typeof process.stdout.columns === "number" && Number.isFinite(process.stdout.columns) @@ -513,8 +482,8 @@ export async function openSettingsModal(ctx: ExtensionCommandContext, controller { getSettings: () => buildInspectorSettings(controller.getConfig(), capabilities), onChange: (id, newValue) => { - const next = applySetting(controller.getConfig(), id, newValue); - controller.setConfig(next, ctx); + const mutation = createSettingMutation(controller.getConfig(), id, newValue); + if (mutation) controller.mutateConfig(mutation, ctx); }, onClose: () => done(), }, @@ -562,7 +531,7 @@ export function handleToolDisplayArgs(args: string, ctx: ExtensionCommandContext } if (normalized === "reset") { - controller.setConfig(getToolDisplayPresetConfig("opencode"), ctx); + controller.mutateConfig({ type: "reset" }, ctx); ctx.ui.notify("Tool display preset reset to opencode.", "info"); return true; } @@ -575,7 +544,7 @@ export function handleToolDisplayArgs(args: string, ctx: ExtensionCommandContext return true; } - controller.setConfig(getToolDisplayPresetConfig(preset), ctx); + controller.mutateConfig({ type: "preset", preset }, ctx); ctx.ui.notify(`Tool display preset set to ${preset}.`, "info"); return true; } diff --git a/src/config-store.ts b/src/config-store.ts index 73faf6a..5321b90 100644 --- a/src/config-store.ts +++ b/src/config-store.ts @@ -18,6 +18,7 @@ import { READ_OUTPUT_MODES, SEARCH_OUTPUT_MODES, type ToolDisplayConfig, + type ToolDisplayConfigOverlay, type BuiltInToolDisplays, } from "./types.js"; import { toRecord } from "./tool-metadata.js"; @@ -289,3 +290,96 @@ export function saveToolDisplayConfig(config: ToolDisplayConfig, configFile = CO export function getToolDisplayConfigPath(): string { return CONFIG_FILE; } + +/** + * Extract only the top-level keys explicitly present in a raw config object. + * Unlike normalizeToolDisplayConfig, this does NOT fill defaults — + * missing keys stay absent so the caller can merge without overwriting + * unrelated global settings. + */ +function extractExplicitFields(raw: unknown): ToolDisplayConfigOverlay { + if (typeof raw !== "object" || raw === null) return {}; + const result: ToolDisplayConfigOverlay = {}; + for (const key of Object.keys(raw as Record)) { + if (key in (raw as Record)) { + (result as Record)[key] = (raw as Record)[key]; + } + } + return result; +} + +/** + * Read a project-local config file. Returns only the fields explicitly + * present in the JSON, without filling defaults. Only call this when the + * project is trusted. + */ +export function readProjectToolDisplayConfig( + projectConfigFile: string, +): { config: ToolDisplayConfigOverlay | undefined; error?: string } { + if (!existsSync(projectConfigFile)) { + return { config: undefined }; + } + try { + const rawText = readFileSync(projectConfigFile, "utf-8"); + const rawConfig = JSON.parse(rawText) as unknown; + return { config: extractExplicitFields(rawConfig) }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return { + config: undefined, + error: `Failed to parse project config ${projectConfigFile}: ${message}`, + }; + } +} + +const NESTED_OBJECT_KEYS: ReadonlySet = new Set(["builtInToolDisplays", "customToolOverrides"]); + +function mergeNestedConfigField( + key: string, + globalValue: unknown, + projectValue: Record, +): Record { + const merged = { ...toRecord(globalValue) }; + for (const [nestedKey, nestedValue] of Object.entries(projectValue)) { + if (nestedValue === undefined) continue; + merged[nestedKey] = key === "customToolOverrides" + && nestedValue !== null + && typeof nestedValue === "object" + && !Array.isArray(nestedValue) + ? { ...toRecord(merged[nestedKey]), ...nestedValue } + : nestedValue; + } + return merged; +} + +/** + * Merge a project-local partial config over a global config. Only keys + * explicitly present in the project config override the global values. + * Nested objects (builtInToolDisplays, customToolOverrides) are deep-merged + * so project overrides of one nested field don't reset unrelated global fields. + * The result is re-normalized to clamp project values to valid ranges. + */ +export function applyToolDisplayConfigPatch( + baseConfig: ToolDisplayConfig, + patch: ToolDisplayConfigOverlay, +): ToolDisplayConfig { + const merged = { ...baseConfig } as Record; + const project = patch as Record; + for (const key of Object.keys(project)) { + const projectValue = project[key]; + if (projectValue === undefined) continue; + if (NESTED_OBJECT_KEYS.has(key) && typeof projectValue === "object" && projectValue !== null && !Array.isArray(projectValue)) { + merged[key] = mergeNestedConfigField(key, merged[key], projectValue as Record); + } else { + merged[key] = projectValue; + } + } + return normalizeToolDisplayConfig(merged); +} + +export function mergeProjectConfig( + globalConfig: ToolDisplayConfig, + projectConfig: ToolDisplayConfigOverlay, +): ToolDisplayConfig { + return applyToolDisplayConfigPatch(globalConfig, projectConfig); +} diff --git a/src/debug-logger.ts b/src/debug-logger.ts index a8447fb..f0b1d05 100644 --- a/src/debug-logger.ts +++ b/src/debug-logger.ts @@ -1,12 +1,11 @@ import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs"; import { appendFile } from "node:fs/promises"; import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; +import { getToolDisplayConfigPath } from "./config-store.js"; import { toRecord } from "./tool-metadata.js"; -const EXTENSION_ROOT = join(dirname(fileURLToPath(import.meta.url)), ".."); -const DEFAULT_DEBUG_CONFIG_FILE = join(EXTENSION_ROOT, "config.json"); -const DEFAULT_DEBUG_DIR = join(EXTENSION_ROOT, "debug"); +const DEFAULT_DEBUG_CONFIG_FILE = getToolDisplayConfigPath(); +const DEFAULT_DEBUG_DIR = join(dirname(DEFAULT_DEBUG_CONFIG_FILE), "debug"); const DEFAULT_DEBUG_LOG_FILE = join(DEFAULT_DEBUG_DIR, "debug.log"); const DEFAULT_DEBUG_CONFIG_CACHE_TTL_MS = 1_000; diff --git a/src/diff-renderer.ts b/src/diff-renderer.ts index 07c7636..8023a7a 100644 --- a/src/diff-renderer.ts +++ b/src/diff-renderer.ts @@ -1940,32 +1940,28 @@ function applyLineLimit( if (limit === 0) return rows.map((row) => clampDiffLineToWidth(row.text, width)); const logicalLineIds = [...new Set(rows.flatMap((row) => row.logicalLineId === undefined ? [] : [row.logicalLineId]))]; - let shown: RenderedRow[]; - let remaining: number; - if (expanded || logicalLineIds.length === 0) { - if (rows.length <= limit) return rows.map((row) => clampDiffLineToWidth(row.text, width)); - shown = rows.slice(0, limit); - remaining = rows.length - shown.length; - } else { - if (logicalLineIds.length <= limit) return rows.map((row) => clampDiffLineToWidth(row.text, width)); - const shownLineIds = new Set(logicalLineIds.slice(0, limit)); - let lastShownIndex = -1; - for (let index = 0; index < rows.length; index++) { - if (shownLineIds.has(rows[index]?.logicalLineId ?? -1)) lastShownIndex = index; - } - shown = rows.slice(0, lastShownIndex + 1); - remaining = logicalLineIds.length - shownLineIds.size; - } + if (logicalLineIds.length <= limit) return rows.map((row) => clampDiffLineToWidth(row.text, width)); + const shownLineIds = new Set(logicalLineIds.slice(0, limit)); const visibleHunks = new Set( - shown + rows + .filter((row) => row.logicalLineId !== undefined && shownLineIds.has(row.logicalLineId)) .map((row) => row.hunkIndex) .filter((hunkIndex): hunkIndex is number => typeof hunkIndex === "number" && hunkIndex > 0), ); + const lastShownContentIndex = rows.reduce( + (lastIndex, row, index) => row.logicalLineId !== undefined && shownLineIds.has(row.logicalLineId) ? index : lastIndex, + -1, + ); + const shown = rows.filter((row, index) => row.logicalLineId !== undefined + ? shownLineIds.has(row.logicalLineId) + : index <= lastShownContentIndex); + const remaining = rows.length - shown.length; const hiddenHunks = Math.max(0, totalHunks - visibleHunks.size); const hintText = buildCollapsedDiffHintText( { remainingLines: remaining, hiddenHunks, + expanded, }, width, DIFF_WIDTH_OPS, diff --git a/src/disposable.ts b/src/disposable.ts index 86927fa..3960e5a 100644 --- a/src/disposable.ts +++ b/src/disposable.ts @@ -11,10 +11,6 @@ export function registerCleanup(callback: () => void): void { cleanupCallbacks.push(callback); } -export function registerTimer(timer: ReturnType | ReturnType): void { - registerCleanup(() => clearInterval(timer as ReturnType)); -} - export function registerSessionCleanup(callback: () => void): () => void { sessionCleanupCallbacks.add(callback); return () => sessionCleanupCallbacks.delete(callback); diff --git a/src/index.ts b/src/index.ts index 9b0f1fb..29885c8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,14 @@ -import type { - ExtensionAPI, - ExtensionCommandContext, +import { + type ExtensionAPI, + type ExtensionCommandContext, + CONFIG_DIR_NAME, } from "@earendil-works/pi-coding-agent"; +import { join } from "node:path"; import { + applyToolDisplayConfigPatch, loadToolDisplayConfig, - normalizeToolDisplayConfig, + readProjectToolDisplayConfig, + mergeProjectConfig, saveToolDisplayConfig, } from "./config-store.js"; import { @@ -13,72 +17,141 @@ import { type ToolDisplayCapabilities, } from "./capabilities.js"; import { registerToolDisplayApi } from "./tool-overrides.js"; -import { registerToolExecutionPatch } from "./tool-execution-patch.js"; -import { disposeAll, disposeSession, resetDisposed } from "./disposable.js"; +import { invalidateToolExecutionRows, registerToolExecutionPatch } from "./tool-execution-patch.js"; +import { disposeAll, registerCleanup, resetDisposed } from "./disposable.js"; import registerNativeUserMessageBox from "./user-message-box-native.js"; -import type { ToolDisplayConfig } from "./types.js"; -import { registerToolDisplayCommand } from "./config-command.js"; +import type { UserMessageTheme } from "./user-message-box-renderer.js"; +import type { ToolDisplayConfig, ToolDisplayConfigOverlay } from "./types.js"; +import { + registerToolDisplayCommand, + type ToolDisplayConfigMutation, +} from "./config-command.js"; +import { getToolDisplayPresetConfig } from "./presets.js"; + +const RELOAD_STATE = Symbol.for("pi-tool-display.reload-state.v1"); +interface ReloadState { + projectConfigPath?: string; + theme?: UserMessageTheme; + lastShutdownReason?: string; +} +type GlobalWithReloadState = typeof globalThis & { [RELOAD_STATE]?: ReloadState }; export default function toolDisplayExtension(pi: ExtensionAPI): void { const initial = loadToolDisplayConfig(); - if (!initial.config.enabled) { - return; - } - - resetDisposed(); - - pi.on("session_shutdown", (event: { reason: string }) => { - disposeSession(); - if (event.reason === "reload" || event.reason === "quit") disposeAll(); - }); - - let config: ToolDisplayConfig = initial.config; - let pendingLoadError = initial.error; - let capabilities: ToolDisplayCapabilities = { - hasRtkOptimizer: false, - }; + const globalWithReloadState = globalThis as GlobalWithReloadState; + const reloadState = globalWithReloadState[RELOAD_STATE]; + const ordinarySessionTransition = ["new", "resume", "fork"].includes(reloadState?.lastShutdownReason ?? ""); + if (!ordinarySessionTransition) resetDisposed(); + const reloadedProject = reloadState?.projectConfigPath + ? readProjectToolDisplayConfig(reloadState.projectConfigPath) + : undefined; + let globalConfig: ToolDisplayConfig = initial.config; + let projectOverlay: ToolDisplayConfigOverlay | undefined = reloadedProject?.config; + let mergedConfig: ToolDisplayConfig = projectOverlay + ? mergeProjectConfig(globalConfig, projectOverlay) + : globalConfig; + let pendingLoadError = initial.error ?? reloadedProject?.error; + let capabilities: ToolDisplayCapabilities = { hasRtkOptimizer: false }; let effectiveConfig: ToolDisplayConfig | undefined; + let activeTheme: UserMessageTheme | undefined = reloadState?.theme; + let disposeSessionInstallation: (() => void) | undefined; const refreshCapabilities = (): void => { capabilities = detectToolDisplayCapabilities(pi, process.cwd()); effectiveConfig = undefined; }; - - const getConfig = (): ToolDisplayConfig => config; + const getConfig = (): ToolDisplayConfig => mergedConfig; const getCapabilities = (): ToolDisplayCapabilities => capabilities; const getEffectiveConfig = (): ToolDisplayConfig => - effectiveConfig ??= applyCapabilityConfigGuards(config, capabilities); + effectiveConfig ??= applyCapabilityConfigGuards(mergedConfig, capabilities); - const setConfig = ( - next: ToolDisplayConfig, + const uninstallSession = (): void => { + invalidateToolExecutionRows(); + disposeSessionInstallation?.(); + disposeSessionInstallation = undefined; + }; + + const installSession = (): void => { + uninstallSession(); + if (!mergedConfig.enabled) return; + const disposers = [ + registerToolExecutionPatch(pi, getEffectiveConfig), + registerNativeUserMessageBox(pi, getConfig, activeTheme), + ]; + let disposed = false; + disposeSessionInstallation = () => { + if (disposed) return; + disposed = true; + for (let index = disposers.length - 1; index >= 0; index--) disposers[index](); + }; + }; + + const mutateConfig = ( + mutation: ToolDisplayConfigMutation, ctx: ExtensionCommandContext, ): void => { - const normalized = normalizeToolDisplayConfig(next); - config = normalized; + globalConfig = mutation.type === "patch" + ? applyToolDisplayConfigPatch(globalConfig, mutation.patch) + : getToolDisplayPresetConfig(mutation.type === "preset" ? mutation.preset : "opencode"); + mergedConfig = projectOverlay ? mergeProjectConfig(globalConfig, projectOverlay) : globalConfig; effectiveConfig = undefined; - const saved = saveToolDisplayConfig(normalized); - if (!saved.success && saved.error) { - ctx.ui.notify(saved.error, "error"); - } - + const saved = saveToolDisplayConfig(globalConfig); + if (!saved.success && saved.error) ctx.ui.notify(saved.error, "error"); + if (!mergedConfig.enabled) uninstallSession(); + else if (!disposeSessionInstallation) installSession(); + else invalidateToolExecutionRows(); }; + registerCleanup(uninstallSession); registerToolDisplayApi(getEffectiveConfig); - registerToolExecutionPatch(pi, getEffectiveConfig); - registerNativeUserMessageBox(pi, getConfig); + registerToolDisplayCommand(pi, { getConfig, mutateConfig, getCapabilities }); + // Reload replaces the extension factory without a new session_start event. + // Install from global config immediately; session_start replaces it with the + // trusted project-effective installation when available. + installSession(); - registerToolDisplayCommand(pi, { getConfig, setConfig, getCapabilities }); + pi.on("session_shutdown", (event: { reason: string }) => { + uninstallSession(); + if (event.reason === "reload") { + globalWithReloadState[RELOAD_STATE] = { + ...globalWithReloadState[RELOAD_STATE], + lastShutdownReason: event.reason, + }; + } else { + activeTheme = undefined; + globalWithReloadState[RELOAD_STATE] = { lastShutdownReason: event.reason }; + } + if (event.reason === "reload" || event.reason === "quit") disposeAll(); + }); pi.on("session_start", async (_event, ctx) => { + uninstallSession(); refreshCapabilities(); + activeTheme = ctx.ui?.theme as unknown as UserMessageTheme | undefined; if (pendingLoadError) { - ctx.ui.notify(pendingLoadError, "warning"); + ctx.ui?.notify?.(pendingLoadError, "warning"); pendingLoadError = undefined; } + + const fresh = loadToolDisplayConfig(); + globalConfig = fresh.config; + projectOverlay = undefined; + let projectConfigPath: string | undefined; + if (ctx.isProjectTrusted()) { + projectConfigPath = join(ctx.cwd, CONFIG_DIR_NAME, "extensions", "pi-tool-display", "config.json"); + const projectResult = readProjectToolDisplayConfig(projectConfigPath); + projectOverlay = projectResult.config; + if (projectResult.error) ctx.ui?.notify?.(projectResult.error, "warning"); + } + globalWithReloadState[RELOAD_STATE] = { projectConfigPath, theme: activeTheme }; + mergedConfig = projectOverlay ? mergeProjectConfig(globalConfig, projectOverlay) : globalConfig; + effectiveConfig = undefined; + installSession(); }); pi.on("before_agent_start", async () => { + if (!mergedConfig.enabled) return; refreshCapabilities(); }); } diff --git a/src/line-width-safety.ts b/src/line-width-safety.ts index 05ab528..82e9bc3 100644 --- a/src/line-width-safety.ts +++ b/src/line-width-safety.ts @@ -9,6 +9,7 @@ export interface WidthMeasurementOps { export interface CollapsedDiffHintOptions { remainingLines: number; hiddenHunks: number; + expanded?: boolean; } function guardSafeWidth(width: number): number | undefined { @@ -63,19 +64,15 @@ export function buildCollapsedDiffHintText( ops: WidthMeasurementOps, ): string { return renderWithSafeWidth(width, "", (safeWidth) => { - const remainingText = `${options.remainingLines} more ${pluralize(options.remainingLines, "diff line")}`; + const remainingText = `${options.remainingLines} more ${pluralize(options.remainingLines, "visual diff line")}`; const hiddenHunksText = options.hiddenHunks > 0 ? `${options.hiddenHunks} more ${pluralize(options.hiddenHunks, "hunk")}` : undefined; - const shortRemainingText = `${options.remainingLines} more ${pluralize(options.remainingLines, "line")}`; - const shortHiddenHunksText = options.hiddenHunks > 0 - ? `${options.hiddenHunks} ${pluralize(options.hiddenHunks, "hunk")}` - : undefined; + const action = options.expanded ? "display capped" : "Ctrl+O to expand"; const candidates = [ - `… (${[remainingText, hiddenHunksText, "Ctrl+O to expand"].filter(Boolean).join(" • ")})`, + `… (${[remainingText, hiddenHunksText, action].filter(Boolean).join(" • ")})`, `… (${[remainingText, hiddenHunksText].filter(Boolean).join(" • ")})`, - `… (${[shortRemainingText, shortHiddenHunksText].filter(Boolean).join(" • ")})`, options.hiddenHunks > 0 ? `… (+${options.remainingLines} • +${options.hiddenHunks}h)` : `… (+${options.remainingLines})`, diff --git a/src/pi-host-adapter.ts b/src/pi-host-adapter.ts index c7fe31d..1c8fd9c 100644 --- a/src/pi-host-adapter.ts +++ b/src/pi-host-adapter.ts @@ -3,6 +3,7 @@ import type { ToolDisplayResolver } from "./tool-display-resolver.js"; type RendererSelector = (this: ToolRowHost, ...args: any[]) => ((...args: any[]) => any) | undefined; interface ToolRowHost { toolName?: string; + invalidate?: () => void; args?: Record; toolDefinition?: Record; builtInToolDefinition?: Record; @@ -13,17 +14,19 @@ interface Installation { resolver: ToolDisplayResolver; patchedCall: RendererSelector; patchedResult: RendererSelector; + active: boolean; + rows: Set; + owner: object; } const STATE = Symbol.for("pi-tool-display.piHostAdapter.v1"); type HostPrototype = ToolRowHost & { getCallRenderer?: RendererSelector; getResultRenderer?: RendererSelector; [STATE]?: Installation }; export interface PiHostAdapterInstallation { readonly installed: boolean; dispose(): void } const supportedVersion = (version: string) => { - if (["0.74.0", "0.80.3"].includes(version)) return true; const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version); if (!match) return false; const [major, minor, patch] = match.slice(1).map(Number); - return major > 0 || (major === 0 && (minor > 81 || (minor === 81 && patch >= 1))); + return major > 0 || minor > 81 || (minor === 81 && patch >= 1); }; const noopInstallation = (): PiHostAdapterInstallation => ({ installed: false, dispose() {} }); @@ -46,8 +49,10 @@ export function installPiHostAdapter( function install(prototype: HostPrototype, resolver: ToolDisplayResolver, piVersion: string): PiHostAdapterInstallation { const existing = ownState(prototype); if (existing && ownValue(prototype, "getCallRenderer") === existing.patchedCall && ownValue(prototype, "getResultRenderer") === existing.patchedResult) { + const owner = {}; existing.resolver = resolver; - return { installed: true, dispose: () => dispose(prototype, existing) }; + existing.owner = owner; + return { installed: true, dispose: () => dispose(prototype, existing, owner) }; } const call = Object.getOwnPropertyDescriptor(prototype, "getCallRenderer"); const result = Object.getOwnPropertyDescriptor(prototype, "getResultRenderer"); @@ -63,13 +68,18 @@ function install(prototype: HostPrototype, resolver: ToolDisplayResolver, piVers label: typeof instance.toolDefinition?.label === "string" ? instance.toolDefinition.label : undefined, builtIn: instance.builtInToolDefinition?.name === (instance.toolDefinition?.name ?? instance.toolName), }); - const state = { call, result, resolver } as Installation; + const owner = {}; + const state = { call, result, resolver, active: true, rows: new Set(), owner } as Installation; const patchedCall: RendererSelector = function (...args: any[]) { const native = originalCall.apply(this, args); + if (!state.active) return native; + state.rows.add(this); return state.resolver.resolve(row(this), { call: native }).call; }; const patchedResult: RendererSelector = function (...args: any[]) { const native = originalResult.apply(this, args); + if (!state.active) return native; + state.rows.add(this); return state.resolver.resolve(row(this), { result: native }).result; }; state.patchedCall = patchedCall; @@ -81,9 +91,9 @@ function install(prototype: HostPrototype, resolver: ToolDisplayResolver, piVers Object.defineProperty(prototype, "getResultRenderer", { ...result, value: patchedResult }); } catch { rollback(prototype, state); - return { installed: false, dispose: () => dispose(prototype, state) }; + return { installed: false, dispose: () => dispose(prototype, state, owner) }; } - return { installed: true, dispose: () => dispose(prototype, state) }; + return { installed: true, dispose: () => dispose(prototype, state, owner) }; } function ownState(prototype: HostPrototype): Installation | undefined { @@ -109,6 +119,15 @@ function rollback(prototype: HostPrototype, state: Installation): void { try { if (ownState(prototype) === state) delete prototype[STATE]; } catch {} } -function dispose(prototype: HostPrototype, state: Installation): void { +function dispose(prototype: HostPrototype, state: Installation, owner: object): void { + if (state.owner !== owner) return; + state.active = false; + state.rows.clear(); rollback(prototype, state); } + +export function invalidatePiHostAdapterRows(host: object): void { + const state = ownState(host as HostPrototype); + if (!state?.active) return; + for (const row of state.rows) row.invalidate?.(); +} diff --git a/src/renderer-catalog.ts b/src/renderer-catalog.ts index 218f4e6..256cfc0 100644 --- a/src/renderer-catalog.ts +++ b/src/renderer-catalog.ts @@ -35,7 +35,13 @@ export class RendererAdapterConflict extends Error { } } -const producerAdapters = new Map>(); +const PRODUCER_ADAPTERS_KEY = Symbol.for("pi-tool-display.producer-adapters.v1"); +type GlobalWithProducerAdapters = typeof globalThis & { + [PRODUCER_ADAPTERS_KEY]?: Map>; +}; +const globalWithProducerAdapters = globalThis as GlobalWithProducerAdapters; +const producerAdapters = globalWithProducerAdapters[PRODUCER_ADAPTERS_KEY] + ??= new Map>(); function detachRenderer(renderer: ToolRenderer | undefined, contextIndex: number): ToolRenderer | undefined { if (!renderer) return undefined; @@ -198,8 +204,10 @@ export function createRendererCatalog(): RendererCatalog { }; if (row.builtIn) return undefined; const configured = getRuntimeCustomToolOverride(row.toolName, config as ToolDisplayConfig); + const hasExplicitOverride = Object.hasOwn(config.customToolOverrides, row.toolName); + if (hasExplicitOverride && !configured?.enabled) return undefined; const producers = producerAdapters.get(row.toolName); - if (!configured?.enabled && producers && producers.size > 1) { + if (!hasExplicitOverride && producers && producers.size > 1) { throw new RendererAdapterConflict( row.toolName, [...producers.values()].map(({ id, kind }) => ({ id, kind })).sort((a, b) => a.id.localeCompare(b.id) || a.kind.localeCompare(b.kind)), diff --git a/src/tool-display-resolver.ts b/src/tool-display-resolver.ts index b7aca52..579d713 100644 --- a/src/tool-display-resolver.ts +++ b/src/tool-display-resolver.ts @@ -52,6 +52,7 @@ export function createToolDisplayResolver( resolve(row, native) { try { const config = getConfig(); + if (!config.enabled) return native; if (config !== source) { source = config; snapshot = Object.freeze({ diff --git a/src/tool-execution-patch.ts b/src/tool-execution-patch.ts index ca8949b..e9db4cb 100644 --- a/src/tool-execution-patch.ts +++ b/src/tool-execution-patch.ts @@ -1,17 +1,29 @@ import { ToolExecutionComponent, VERSION, type ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { createPiToolDisplayResolver } from "./tool-display-runtime.js"; -import { installPiHostAdapter } from "./pi-host-adapter.js"; +import { installPiHostAdapter, invalidatePiHostAdapterRows } from "./pi-host-adapter.js"; import { logToolDisplayDebug } from "./debug-logger.js"; +import { registerSessionCleanup } from "./disposable.js"; import type { ToolDisplayConfig } from "./types.js"; -export function registerToolExecutionPatch(pi: ExtensionAPI, getConfig: () => ToolDisplayConfig): void { +export function invalidateToolExecutionRows(): void { + invalidatePiHostAdapterRows(ToolExecutionComponent.prototype); +} + +export function registerToolExecutionPatch(_pi: ExtensionAPI, getConfig: () => ToolDisplayConfig): () => void { const installation = installPiHostAdapter( ToolExecutionComponent.prototype, createPiToolDisplayResolver(getConfig), VERSION, message => logToolDisplayDebug(message), ); - pi.on("session_shutdown", (event: { reason?: string }) => { - if (event.reason === "reload" || event.reason === "quit") installation.dispose(); - }); + let disposed = false; + let unregister = () => {}; + const dispose = () => { + if (disposed) return; + disposed = true; + unregister(); + installation.dispose(); + }; + unregister = registerSessionCleanup(dispose); + return dispose; } diff --git a/src/tool-overrides.ts b/src/tool-overrides.ts index 52d99a0..a5b5eab 100644 --- a/src/tool-overrides.ts +++ b/src/tool-overrides.ts @@ -18,7 +18,6 @@ import { extractTextOutput, isLikelyQuietCommand, pluralize, - previewLines, sanitizeAnsiForThemedOutput, shortenPath, splitLines, @@ -69,6 +68,7 @@ interface RtkCompactionInfo { const RTK_COMPACTION_LABEL = "compacted by RTK"; const TOOL_DISPLAY_API_KEY = Symbol.for("pi-tool-display.api.v1"); +const TOOL_DISPLAY_API_STATE_KEY = Symbol.for("pi-tool-display.api-state.v1"); const TOOL_DISPLAY_PENDING_DECORATIONS_KEY = Symbol.for("pi-tool-display.pendingDecorations.v1"); type ToolDisplayKind = "read" | "edit" | "mcp" | "generic"; @@ -78,6 +78,8 @@ export interface ToolDisplayAdapter { toolName?: string; kind?: ToolDisplayKind; overrideExistingRenderers?: boolean; + overrideCallRenderer?: boolean; + outputMode?: "hidden" | "summary" | "preview"; pathFields?: string[]; getPath?: (args: unknown) => string | undefined; getEditLineCount?: (args: unknown) => number; @@ -98,8 +100,17 @@ interface PendingToolDisplayDecoration { liveDispose?: () => void; } +interface ToolDisplayApiState { + api: ToolDisplayApi; + getConfig: ConfigGetter; + disposers: Set<() => void>; + disposed: boolean; + dispose: () => void; +} + type GlobalWithToolDisplayApi = typeof globalThis & { [TOOL_DISPLAY_API_KEY]?: ToolDisplayApi; + [TOOL_DISPLAY_API_STATE_KEY]?: ToolDisplayApiState; [TOOL_DISPLAY_PENDING_DECORATIONS_KEY]?: PendingToolDisplayDecoration[]; }; @@ -107,40 +118,8 @@ function formatExpandHint(theme: RenderTheme): string { return theme.fg("muted", " • Ctrl+O to expand"); } -function formatTruncationHint(remaining: number, expanded: boolean, theme: RenderTheme): string { - if (remaining <= 0) { - return ""; - } - const hint = expanded ? "" : " • Ctrl+O to expand"; - return `\n${theme.fg("muted", `... (${remaining} more ${pluralize(remaining, "line")}${hint})`)}`; -} - -function buildPreviewText( - lines: string[], - maxLines: number, - theme: RenderTheme, - expanded: boolean, -): string { - if (lines.length === 0) { - return theme.fg("muted", "↳ (no output)"); - } - - const { shown, remaining } = previewLines(lines, maxLines); - let text = shown - .map((line) => theme.fg("toolOutput", sanitizeAnsiForThemedOutput(line))) - .join("\n"); - text += formatTruncationHint(remaining, expanded, theme); - return text; -} - -function prepareOutputLines( - rawText: string, - options: ToolRenderResultOptions, -): string[] { - return compactOutputLines(splitLines(rawText), { - expanded: options.expanded, - maxCollapsedConsecutiveEmptyLines: 1, - }); +function prepareOutputLines(rawText: string): string[] { + return splitLines(rawText); } function formatBashNoOutputLine( @@ -338,11 +317,11 @@ function handlePartialResult( return options.isPartial ? partialResultText(theme, message) : undefined; } -function renderSearchPreview(ctx: PreviewHintContext, expandedOnly = false): Text { +function renderSearchPreview(ctx: PreviewHintContext, expandedOnly = false): Component { return renderPreviewText(ctx.lines, ctx.config, ctx.theme, ctx.options, (p) => appendPreviewHints(p, ctx), expandedOnly); } -function renderMcpPreview(ctx: McpPreviewHintContext, expandedOnly = false): Text { +function renderMcpPreview(ctx: McpPreviewHintContext, expandedOnly = false): Component { return renderPreviewText(ctx.lines, ctx.config, ctx.theme, ctx.options, (p) => appendMcpPreviewHints(p, ctx), expandedOnly); } @@ -367,30 +346,6 @@ function formatRtkSummarySuffix(params: RtkHintParams): string { }); } -function getExpandedPreviewLineLimit( - lines: string[], - config: ToolDisplayConfig, -): number { - const limit = Math.max(0, config.expandedPreviewMaxLines); - if (limit === 0) { - return lines.length; - } - return Math.min(lines.length, limit); -} - -function formatExpandedPreviewCapHint( - lines: string[], - config: ToolDisplayConfig, - theme: RenderTheme, -): string { - const cap = Math.max(0, config.expandedPreviewMaxLines); - if (cap === 0 || lines.length <= cap) { - return ""; - } - - return `\n${theme.fg("warning", `(display capped at ${cap} lines by tool-display setting)`)}`; -} - function formatRtkPreviewHint(params: RtkHintParams): string { const { theme } = params; return withRtkCompactionInfo(params, (info) => { @@ -421,15 +376,11 @@ function formatRtkPreviewHint(params: RtkHintParams): string { } function appendRtkAndExpandedHints(preview: string, ctx: PreviewHintContext): string { - preview += formatRtkPreviewHint(ctx); - if (ctx.options.expanded) { - preview += formatExpandedPreviewCapHint(ctx.lines, ctx.config, ctx.theme); - } - return preview; + return preview + formatRtkPreviewHint(ctx); } function appendMcpPreviewHints(preview: string, ctx: McpPreviewHintContext): string { - const { config, theme, details, lines, options, truncation } = ctx; + const { config, theme, details, truncation } = ctx; if (config.showTruncationHints && (truncation.truncated || truncation.fullOutputPath)) { const hints: string[] = []; if (truncation.truncated) { @@ -451,6 +402,32 @@ function appendPreviewHints(preview: string, ctx: PreviewHintContext): string { return appendRtkAndExpandedHints(preview, ctx); } +/** + * Wrap a VisualLinePreviewComponent with optional hint text below it. + * If hints is empty, returns the component directly. + */ +function wrapComponentWithHints( + component: Component, + hints: string, +): Component { + if (!hints) return component; + const container = new Container(); + container.addChild(component); + container.addChild(textResult(hints)); + return container; +} + +/** + * Visual row budget for expanded previews. Uses expandedPreviewMaxLines as + * a cap to prevent long single-line content from flooding the viewport. + * 0 means unlimited (no cap). + */ +function getExpandedVisualRowCap(config: ToolDisplayConfig): number { + const limit = config.expandedPreviewMaxLines; + if (limit <= 0) return Number.MAX_SAFE_INTEGER; + return limit; +} + function renderPreviewText( lines: string[], config: ToolDisplayConfig, @@ -458,13 +435,16 @@ function renderPreviewText( options: ToolRenderResultOptions, appendHints: (preview: string) => string, expandedOnly: boolean = false, -): Text { - const useExpanded = expandedOnly || options.expanded; - const maxLines = useExpanded - ? getExpandedPreviewLineLimit(lines, config) - : config.previewLines; - const preview = buildPreviewText(lines, maxLines, theme, useExpanded); - return textResult(appendHints(preview)); +): Component { + if (lines.length === 0) { + return textResult(theme.fg("muted", "↳ (no output)") + appendHints("")); + } + + const expanded = expandedOnly || options.expanded; + const visualLimit = expanded ? getExpandedVisualRowCap(config) : config.previewLines; + const preview = new VisualLinePreviewComponent(visualLimit, expanded, theme); + preview.setDisplay(formatPreviewBody(lines, "toolOutput", theme), visualLimit, expanded); + return wrapComponentWithHints(preview, appendHints("")); } function formatReadSummary( @@ -540,17 +520,11 @@ function formatBashTruncationHints( } function getBashPreviewLineLimit( - lines: string[], options: ToolRenderResultOptions, config: ToolDisplayConfig, ): number { - if (options.expanded) { - return getExpandedPreviewLineLimit(lines, config); - } - - return config.bashOutputMode === "opencode" - ? config.bashCollapsedLines - : config.previewLines; + if (options.expanded) return getExpandedVisualRowCap(config); + return config.bashOutputMode === "opencode" ? config.bashCollapsedLines : config.previewLines; } type ToolRenderInput = { @@ -566,6 +540,10 @@ function partialResultText(theme: RenderTheme, label: string): Text { return textResult(theme.fg("warning", label)); } +function formatPreviewBody(lines: string[], color: string, theme: RenderTheme): string { + return theme.fg(color, sanitizeAnsiForThemedOutput(lines.join("\n"))); +} + function renderBashPreviewWithHints( lines: string[], maxLines: number, @@ -573,15 +551,13 @@ function renderBashPreviewWithHints( theme: RenderTheme, options: ToolRenderResultOptions, details: BashToolDetails | undefined, -): Text { - let preview = buildPreviewText(lines, maxLines, theme, options.expanded); - if (config.showTruncationHints) { - preview += formatBashTruncationHints(details, theme); - } - if (options.expanded) { - preview += formatExpandedPreviewCapHint(lines, config, theme); - } - return textResult(preview); +): Component { + const visualLimit = options.expanded ? getExpandedVisualRowCap(config) : maxLines; + const preview = new VisualLinePreviewComponent(visualLimit, options.expanded, theme); + preview.setDisplay(formatPreviewBody(lines, "toolOutput", theme), visualLimit, options.expanded); + let hints = ""; + if (config.showTruncationHints) hints += formatBashTruncationHints(details, theme); + return wrapComponentWithHints(preview, hints); } function renderBashVisualPreview( @@ -592,14 +568,10 @@ function renderBashVisualPreview( details: BashToolDetails | undefined, ): Component { const preview = new VisualLinePreviewComponent(maxLines, false, theme); - preview.setDisplay(buildPreviewText(lines, lines.length, theme, false), maxLines, false); - const truncationHints = config.showTruncationHints ? formatBashTruncationHints(details, theme) : ""; - if (!truncationHints) return preview; - - const container = new Container(); - container.addChild(preview); - container.addChild(textResult(truncationHints.slice(1))); - return container; + preview.setDisplay(formatPreviewBody(lines, "toolOutput", theme), maxLines, false); + let hints = ""; + if (config.showTruncationHints) hints += formatBashTruncationHints(details, theme); + return wrapComponentWithHints(preview, hints); } function prepareBashLivePreview( @@ -607,15 +579,11 @@ function prepareBashLivePreview( options: ToolRenderResultOptions, config: ToolDisplayConfig, ): { lines: string[]; maxLines: number } | undefined { - const lines = prepareOutputLines(rawOutput, options); + const lines = prepareOutputLines(rawOutput); if (lines.length === 0) { return undefined; } - const maxLines = getBashPreviewLineLimit(lines, options, config); - if (!options.expanded && maxLines === 0) { - return undefined; - } - return { lines, maxLines }; + return { lines, maxLines: getBashPreviewLineLimit(options, config) }; } function renderBashLivePreview( @@ -642,26 +610,24 @@ function renderBashErrorResult( theme: RenderTheme, details: BashToolDetails | undefined, ): Component { - const lines = prepareOutputLines(rawOutput, options); + const lines = prepareOutputLines(rawOutput); const container = new Container(); container.addChild(textResult(theme.fg("error", options.expanded || config.bashErrorOutputMode !== "summary" ? "↳ command failed" : `↳ command failed · ${lines.length} ${pluralize(lines.length, "line")} returned`))); if (lines.length > 0 && (options.expanded || config.bashErrorOutputMode !== "summary")) { - const maxLines = options.expanded ? getExpandedPreviewLineLimit(lines, config) : lines.length; - const { shown, remaining } = previewLines(lines, maxLines); - let body = shown.map((line) => theme.fg("error", sanitizeAnsiForThemedOutput(line))).join("\n"); - body += formatTruncationHint(remaining, options.expanded, theme); - if (config.showTruncationHints) body += formatBashTruncationHints(details, theme); - if (options.expanded) body += formatExpandedPreviewCapHint(lines, config, theme); - - if (!options.expanded && config.bashErrorOutputMode === "preview") { - const preview = new VisualLinePreviewComponent(config.bashErrorPreviewLines, false, theme); - preview.setDisplay(body, config.bashErrorPreviewLines, false); - container.addChild(preview); + const body = formatPreviewBody(lines, "error", theme); + let hints = ""; + if (config.showTruncationHints) hints += formatBashTruncationHints(details, theme); + + if (config.bashErrorOutputMode === "preview" || options.expanded) { + const visualLimit = options.expanded ? getExpandedVisualRowCap(config) : config.bashErrorPreviewLines; + const preview = new VisualLinePreviewComponent(visualLimit, options.expanded, theme); + preview.setDisplay(body, visualLimit, options.expanded); + container.addChild(wrapComponentWithHints(preview, hints)); } else { - container.addChild(textResult(body)); + container.addChild(textResult(body + hints)); } } @@ -680,25 +646,20 @@ export function renderBashResult( if (options.isPartial) return renderBashLivePreview(rawOutput, options, config, theme, details); if (isToolError(result, context)) return renderBashErrorResult(rawOutput, options, config, theme, details); - const lines = prepareOutputLines(rawOutput, options); + const lines = prepareOutputLines(rawOutput); if (lines.length === 0) { let text = formatBashNoOutputLine(getStringField(context?.args, "command"), theme); if (config.showTruncationHints) text += formatBashTruncationHints(details, theme); return textResult(text); } if (config.bashOutputMode === "summary") { - if (options.expanded) return renderBashPreviewWithHints(lines, getExpandedPreviewLineLimit(lines, config), config, theme, options, details); + if (options.expanded) return renderBashPreviewWithHints(lines, config.previewLines, config, theme, options, details); let summary = formatBashSummary(lines, details, theme, config.showTruncationHints) + formatExpandHint(theme); if (config.showTruncationHints) summary += formatBashTruncationHints(details, theme); return textResult(summary); } if (config.bashOutputMode === "preview") { - return renderBashPreviewWithHints(lines, options.expanded ? getExpandedPreviewLineLimit(lines, config) : config.previewLines, config, theme, options, details); - } - if (!options.expanded && config.bashCollapsedLines === 0) { - let hidden = theme.fg("muted", "↳ output hidden"); - if (config.showTruncationHints) hidden += formatBashTruncationHints(details, theme); - return textResult(hidden); + return renderBashPreviewWithHints(lines, config.previewLines, config, theme, options, details); } if (options.expanded) return renderBashPreviewWithHints(lines, lines.length, config, theme, options, details); return renderBashVisualPreview(lines, config.bashCollapsedLines, config, theme, details); @@ -713,12 +674,12 @@ export function renderSearchResult( unitLabel: string, details: GrepToolDetails | FindToolDetails | LsToolDetails | undefined, pluralLabel?: string, -): Text { +): Component { if (options.isPartial) { return partialResultText(theme, "running..."); } - const lines = prepareOutputLines(extractTextOutput(result), options); + const lines = prepareOutputLines(extractTextOutput(result)); if (config.searchOutputMode === "hidden") { return textResult(""); @@ -825,7 +786,7 @@ function renderMcpResult( options: ToolRenderResultOptions, config: ToolDisplayConfig, theme: RenderTheme, -): Text { +): Component { const partial = handlePartialResult(options, theme, "running..."); if (partial) { return partial; @@ -835,7 +796,7 @@ function renderMcpResult( return textResult(""); } - const lines = prepareOutputLines(extractTextOutput(result), options); + const lines = prepareOutputLines(extractTextOutput(result)); const truncation = getMcpTruncationDetails(result.details); const mcpCtx: McpPreviewHintContext = { lines, config, theme, options, details: result.details, truncation }; @@ -914,7 +875,7 @@ export function renderCustomToolResult( config: ToolDisplayConfig, outputMode: CustomToolOverrideConfig["outputMode"], theme: RenderTheme, -): Text { +): Component { return renderMcpResult( result as ToolRenderInput, options, @@ -967,7 +928,7 @@ export function renderReadDisplayResult( options: ToolRenderResultOptions, config: ToolDisplayConfig, theme: RenderTheme, - ): Text { + ): Component { if (options.isPartial) { return partialResultText(theme, "reading..."); } @@ -978,7 +939,7 @@ export function renderReadDisplayResult( const details = result.details as ReadToolDetails | undefined; const rawOutput = extractTextOutput(result); - const lines = prepareOutputLines(rawOutput, options); + const lines = prepareOutputLines(rawOutput); const hintCtx: PreviewHintContext = { lines, config, theme, options, details }; if (config.readOutputMode === "summary") { @@ -1040,7 +1001,8 @@ function toProducerAdapter(toolName: string, adapter: ToolDisplayAdapter = {}, g id: adapter.id ?? toolName, toolName, kind: kind === "mcp" ? "mcp" : "generic", - overrideCallRenderer: adapter.overrideExistingRenderers, + outputMode: adapter.outputMode, + overrideCallRenderer: adapter.overrideCallRenderer ?? adapter.overrideExistingRenderers, renderCall: adapter.renderCall ?? (kind === "read" ? (args, theme) => renderReadDisplayCall(args, theme, adapter) : kind === "edit" @@ -1071,21 +1033,21 @@ function drainPendingToolDisplayDecorations(api: ToolDisplayApi, getConfig: Conf entries.length = 0; } -function installToolDisplayApi(_getConfig: ConfigGetter): ToolDisplayApi { - const disposers = new Set<() => void>(); +function installToolDisplayApi(state: ToolDisplayApiState): ToolDisplayApi { const legacyDisposers = new Map void>(); const api: ToolDisplayApi = { version: 1, registerAdapter(adapter) { + if (state.disposed) throw new Error("Tool display API has been disposed"); const disposeRegistration = registerProducerRendererAdapter(adapter); let disposed = false; const dispose = () => { if (disposed) return; disposed = true; - disposers.delete(dispose); + state.disposers.delete(dispose); disposeRegistration(); }; - disposers.add(dispose); + state.disposers.add(dispose); return dispose; }, decorateTool(tool: T, adapter?: ToolDisplayAdapter): T { @@ -1093,21 +1055,43 @@ function installToolDisplayApi(_getConfig: ConfigGetter): ToolDisplayApi { if (!toolName) throw new Error("Tool display compatibility registration requires tool.name"); const key = `${toolName}:${adapter?.id ?? toolName}`; legacyDisposers.get(key)?.(); - const dispose = api.registerAdapter(toProducerAdapter(toolName, adapter, _getConfig)); + const dispose = api.registerAdapter(toProducerAdapter(toolName, adapter, () => state.getConfig())); legacyDisposers.set(key, dispose); return tool; }, }; (globalThis as GlobalWithToolDisplayApi)[TOOL_DISPLAY_API_KEY] = api; - drainPendingToolDisplayDecorations(api, _getConfig); - registerCleanup(() => { for (const dispose of [...disposers]) dispose(); }); + drainPendingToolDisplayDecorations(api, () => state.getConfig()); return api; } export function registerToolDisplayApi(getConfig: ConfigGetter): void { - const toolDisplayApi = installToolDisplayApi(getConfig); - registerCleanup(() => { - const globalWithApi = globalThis as GlobalWithToolDisplayApi; + const globalWithApi = globalThis as GlobalWithToolDisplayApi; + const existing = globalWithApi[TOOL_DISPLAY_API_STATE_KEY]; + if (existing && globalWithApi[TOOL_DISPLAY_API_KEY] === existing.api) { + existing.getConfig = getConfig; + drainPendingToolDisplayDecorations(existing.api, () => existing.getConfig()); + registerCleanup(existing.dispose); + return; + } + + const state = { + getConfig, + disposers: new Set<() => void>(), + disposed: false, + dispose: () => {}, + } as ToolDisplayApiState; + const toolDisplayApi = installToolDisplayApi(state); + state.api = toolDisplayApi; + let disposed = false; + state.dispose = () => { + if (disposed) return; + disposed = true; + state.disposed = true; + for (const dispose of [...state.disposers]) dispose(); if (globalWithApi[TOOL_DISPLAY_API_KEY] === toolDisplayApi) delete globalWithApi[TOOL_DISPLAY_API_KEY]; - }); + if (globalWithApi[TOOL_DISPLAY_API_STATE_KEY] === state) delete globalWithApi[TOOL_DISPLAY_API_STATE_KEY]; + }; + globalWithApi[TOOL_DISPLAY_API_STATE_KEY] = state; + registerCleanup(state.dispose); } diff --git a/src/types.ts b/src/types.ts index ab5556b..e193d0e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -109,6 +109,16 @@ export const DEFAULT_TOOL_DISPLAY_CONFIG: ToolDisplayConfig = { showRtkCompactionHints: false, }; +export type ToolDisplayConfigPatch = Omit< + Partial, + "builtInToolDisplays" | "customToolOverrides" +> & { + builtInToolDisplays?: Partial; + customToolOverrides?: Record | boolean>; +}; + +export type ToolDisplayConfigOverlay = ToolDisplayConfigPatch; + export interface ConfigLoadResult { config: ToolDisplayConfig; error?: string; diff --git a/src/user-message-box-native.ts b/src/user-message-box-native.ts index d798c89..7a38964 100644 --- a/src/user-message-box-native.ts +++ b/src/user-message-box-native.ts @@ -7,60 +7,27 @@ import { type PatchableUserMessagePrototype, type UserMessageTheme, } from "./user-message-box-renderer.js"; -import { unregisterUserMessageRenderPrototypePatch } from "./user-message-box-patch.js"; import type { ToolDisplayConfig } from "./types.js"; -const registeredNativeUserMessageApis = new WeakSet(); - function getUserMessagePrototype(): PatchableUserMessagePrototype { return UserMessageComponent.prototype as unknown as PatchableUserMessagePrototype; } -function patchUserMessageRender( - getTheme: () => UserMessageTheme | undefined, - isEnabled: () => boolean, -): void { - patchNativeUserMessagePrototype( - getUserMessagePrototype(), - getTheme, - isEnabled, - ); -} - -function restoreUserMessageRender(): void { - unregisterUserMessageRenderPrototypePatch(getUserMessagePrototype()); -} - export default function registerNativeUserMessageBox( - pi: ExtensionAPI, + _pi: ExtensionAPI, getConfig: () => ToolDisplayConfig, -): void { - if (registeredNativeUserMessageApis.has(pi)) { - return; - } - registeredNativeUserMessageApis.add(pi); - - let activeTheme: UserMessageTheme | undefined; - - const getTheme = (): UserMessageTheme | undefined => activeTheme; - const isEnabled = (): boolean => getConfig().enableNativeUserMessageBox; - - patchUserMessageRender(getTheme, isEnabled); - - pi.on("session_shutdown", async (event: { reason?: string }) => { - if (event?.reason !== "reload" && event?.reason !== "quit") return; - restoreUserMessageRender(); - activeTheme = undefined; - registeredNativeUserMessageApis.delete(pi); - }); - - pi.on("before_agent_start", async () => { - patchUserMessageRender(getTheme, isEnabled); - }); - - pi.on("session_start", async (_event, ctx) => { - activeTheme = ctx?.ui?.theme as unknown as UserMessageTheme; - patchUserMessageRender(getTheme, isEnabled); - }); + theme: UserMessageTheme | undefined, +): () => void { + const disposePatch = patchNativeUserMessagePrototype( + getUserMessagePrototype(), + () => theme, + () => getConfig().enableNativeUserMessageBox, + ); + let disposed = false; + return () => { + if (disposed) return; + disposed = true; + disposePatch(); + }; } diff --git a/src/user-message-box-patch.ts b/src/user-message-box-patch.ts index cdb6a1d..938ca6c 100644 --- a/src/user-message-box-patch.ts +++ b/src/user-message-box-patch.ts @@ -1,6 +1,6 @@ export type UserMessageRenderFn = (width: number) => string[]; -const USER_MESSAGE_PATCH_OWNER = {}; +const USER_MESSAGE_PATCH_OWNERS = new WeakSet(); export interface PatchableUserMessagePrototype { render: UserMessageRenderFn; @@ -12,15 +12,32 @@ export interface PatchableUserMessagePrototype { __piUserMessagePatchOwner?: object; __piUserMessageInstalledRender?: UserMessageRenderFn; __piUserMessagePatchState?: { active: boolean }; + __piUserMessagePatchDispose?: () => void; +} + +function clearPatchMetadata(prototype: PatchableUserMessagePrototype): void { + delete prototype.__piUserMessageOriginalRender; + delete prototype.__piUserMessageOriginalRenderDescriptor; + delete prototype.__piUserMessageOriginalRenderWasOwn; + delete prototype.__piUserMessageNativePatched; + delete prototype.__piUserMessagePatchVersion; + delete prototype.__piUserMessagePatchOwner; + delete prototype.__piUserMessageInstalledRender; + delete prototype.__piUserMessagePatchState; + delete prototype.__piUserMessagePatchDispose; } export function unregisterUserMessageRenderPrototypePatch( prototype: PatchableUserMessagePrototype, ): void { - if (prototype.__piUserMessagePatchOwner !== USER_MESSAGE_PATCH_OWNER) return; + if (prototype.__piUserMessagePatchDispose) { + prototype.__piUserMessagePatchDispose(); + return; + } + const owner = prototype.__piUserMessagePatchOwner; + if (owner && !USER_MESSAGE_PATCH_OWNERS.has(owner)) return; const installedRender = prototype.__piUserMessageInstalledRender; - const state = prototype.__piUserMessagePatchState; - if (state) state.active = false; + if (prototype.__piUserMessagePatchState) prototype.__piUserMessagePatchState.active = false; if (prototype.render === installedRender) { if (prototype.__piUserMessageOriginalRenderWasOwn && prototype.__piUserMessageOriginalRenderDescriptor) { Object.defineProperty(prototype, "render", prototype.__piUserMessageOriginalRenderDescriptor); @@ -28,73 +45,66 @@ export function unregisterUserMessageRenderPrototypePatch( delete (prototype as Partial).render; } } - - delete prototype.__piUserMessageOriginalRender; - delete prototype.__piUserMessageOriginalRenderDescriptor; - delete prototype.__piUserMessageOriginalRenderWasOwn; - delete prototype.__piUserMessageNativePatched; - delete prototype.__piUserMessagePatchVersion; - delete prototype.__piUserMessagePatchOwner; - delete prototype.__piUserMessageInstalledRender; - delete prototype.__piUserMessagePatchState; + clearPatchMetadata(prototype); } export function patchUserMessageRenderPrototype( prototype: PatchableUserMessagePrototype, patchVersion: number, buildRender: (originalRender: UserMessageRenderFn) => UserMessageRenderFn, -): void { - if (typeof prototype.render !== "function") { - return; - } - - const previousOriginalRender = prototype.__piUserMessageOriginalRender; - const hasPreviousPatch = typeof previousOriginalRender === "function" - && previousOriginalRender !== prototype.render; - const isCurrentPatch = prototype.__piUserMessagePatchOwner === USER_MESSAGE_PATCH_OWNER; - let restoredStalePatch = false; - - if (hasPreviousPatch && !isCurrentPatch) { - if (prototype.__piUserMessagePatchState) prototype.__piUserMessagePatchState.active = false; - prototype.render = previousOriginalRender; - delete prototype.__piUserMessageOriginalRender; - delete prototype.__piUserMessageOriginalRenderDescriptor; - delete prototype.__piUserMessageOriginalRenderWasOwn; - delete prototype.__piUserMessageNativePatched; - delete prototype.__piUserMessagePatchVersion; - delete prototype.__piUserMessagePatchOwner; - delete prototype.__piUserMessageInstalledRender; - delete prototype.__piUserMessagePatchState; - restoredStalePatch = true; - } +): () => void { + if (typeof prototype.render !== "function") return () => {}; + const currentOwner = prototype.__piUserMessagePatchOwner; if ( - !restoredStalePatch + !currentOwner && prototype.__piUserMessageNativePatched && prototype.__piUserMessagePatchVersion === patchVersion - && typeof prototype.__piUserMessageOriginalRender === "function" - ) { - return; + && prototype.__piUserMessageOriginalRender + ) return () => {}; + if (currentOwner && USER_MESSAGE_PATCH_OWNERS.has(currentOwner) && prototype.__piUserMessagePatchDispose) { + prototype.__piUserMessagePatchDispose(); } - if (!prototype.__piUserMessageOriginalRender) { - prototype.__piUserMessageOriginalRender = prototype.render; - const descriptor = Object.getOwnPropertyDescriptor(prototype, "render"); - prototype.__piUserMessageOriginalRenderWasOwn = !!descriptor; - prototype.__piUserMessageOriginalRenderDescriptor = descriptor; + const previousOriginalRender = prototype.__piUserMessageOriginalRender; + if (typeof previousOriginalRender === "function" && previousOriginalRender !== prototype.render) { + if (prototype.__piUserMessagePatchState) prototype.__piUserMessagePatchState.active = false; + prototype.render = previousOriginalRender; + clearPatchMetadata(prototype); } - const originalRender = prototype.__piUserMessageOriginalRender; - if (!originalRender) return; - - const patchedRender = buildRender(originalRender); + const originalRender = prototype.render; + const originalDescriptor = Object.getOwnPropertyDescriptor(prototype, "render"); + const originalWasOwn = !!originalDescriptor; + const owner = {}; + USER_MESSAGE_PATCH_OWNERS.add(owner); const state = { active: true }; - prototype.render = function (this: unknown, width: number): string[] { + const patchedRender = buildRender(originalRender); + const installedRender: UserMessageRenderFn = function (this: unknown, width: number): string[] { return (state.active ? patchedRender : originalRender).call(this, width); }; - prototype.__piUserMessageInstalledRender = prototype.render; + let disposed = false; + const dispose = (): void => { + if (disposed) return; + disposed = true; + state.active = false; + if (prototype.__piUserMessagePatchOwner !== owner) return; + if (prototype.render === installedRender) { + if (originalWasOwn && originalDescriptor) Object.defineProperty(prototype, "render", originalDescriptor); + else delete (prototype as Partial).render; + } + clearPatchMetadata(prototype); + }; + + prototype.render = installedRender; + prototype.__piUserMessageOriginalRender = originalRender; + prototype.__piUserMessageOriginalRenderDescriptor = originalDescriptor; + prototype.__piUserMessageOriginalRenderWasOwn = originalWasOwn; + prototype.__piUserMessageInstalledRender = installedRender; prototype.__piUserMessagePatchState = state; prototype.__piUserMessageNativePatched = true; prototype.__piUserMessagePatchVersion = patchVersion; - prototype.__piUserMessagePatchOwner = USER_MESSAGE_PATCH_OWNER; + prototype.__piUserMessagePatchOwner = owner; + prototype.__piUserMessagePatchDispose = dispose; + return dispose; } diff --git a/src/user-message-box-renderer.ts b/src/user-message-box-renderer.ts index dab725f..6e8a03e 100644 --- a/src/user-message-box-renderer.ts +++ b/src/user-message-box-renderer.ts @@ -354,11 +354,11 @@ export function patchNativeUserMessagePrototype( prototype: PatchableUserMessagePrototype, getTheme: () => UserMessageTheme | undefined, isEnabled: () => boolean, -): void { +): () => void { const finalOutputCache = new WeakMap(); const originalBodyLineCache = new WeakMap(); - patchUserMessageRenderPrototype( + return patchUserMessageRenderPrototype( prototype, USER_MESSAGE_PATCH_VERSION, (originalRender) => diff --git a/tests/active-backlog-red.test.ts b/tests/active-backlog-red.test.ts index 055d75b..c47f8a1 100644 --- a/tests/active-backlog-red.test.ts +++ b/tests/active-backlog-red.test.ts @@ -37,7 +37,7 @@ function buildLargeUnifiedDiff(changeCount: number): string { return lines.join("\n"); } -test("issue #23: expanded large diffs stay bounded for small tmux panes", () => { +test("issue #23: expanded large diffs cap logical Diff lines in small tmux panes", () => { const component = renderEditDiffResult( { diff: buildLargeUnifiedDiff(80) }, { expanded: true, filePath: "large.txt" }, @@ -47,13 +47,16 @@ test("issue #23: expanded large diffs stay bounded for small tmux panes", () => ); const lines = renderInsideToolBox(component, 100); + const plain = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); assert.ok( - lines.length <= diffConfig.expandedPreviewMaxLines + 8, - `expected expanded large diff to stay bounded near ${diffConfig.expandedPreviewMaxLines} lines, rendered ${lines.length}`, + lines.length <= diffConfig.expandedPreviewMaxLines + 10, + `expected expanded large diff to stay bounded near ${diffConfig.expandedPreviewMaxLines} logical lines, rendered ${lines.length}`, ); + assert.ok(plain.some((line) => line.includes("new line 32"))); + assert.ok(plain.every((line) => !line.includes("old line 33"))); assert.ok( - lines.some((line) => /remaining|omitted|collapsed|more/i.test(line)), - "expected a visible truncation hint for omitted large-diff content", + plain.some((line) => /more visual diff lines/i.test(line)), + "expected a visual Diff-line omission hint", ); }); diff --git a/tests/bash-display.test.ts b/tests/bash-display.test.ts index 6d3865a..b9f7d02 100644 --- a/tests/bash-display.test.ts +++ b/tests/bash-display.test.ts @@ -5,8 +5,6 @@ import { renderBashCall } from "../src/bash-display.ts"; // ─── Test Helpers ──────────────────────────────────────────────────────────── -const BASH_SPINNER_STATE_KEY = "__piToolDisplayBashSpinner"; - interface BashCallRenderTheme { fg(color: string, text: string): string; bold(text: string): string; @@ -16,9 +14,7 @@ interface BashCallRenderContextLike { executionStarted: boolean; isPartial: boolean; expanded?: boolean; - invalidate(): void; lastComponent?: unknown; - state?: unknown; } /** Pass-through theme that returns text unchanged */ @@ -42,7 +38,6 @@ function makeContext(overrides: Partial = {}): BashCa return { executionStarted: false, isPartial: false, - invalidate: () => {}, ...overrides, }; } @@ -51,43 +46,6 @@ function renderedText(component: Component, width = 120): string { return component.render(width).map((line) => line.trimEnd()).join("\n").trim(); } -/** - * Create a spinner context and render. Returns the component plus a cleanup - * function that transitions to shouldSpin=false (stopping the interval timer). - */ -function createSpinningBashCall( - args: { command?: string; timeout?: number }, - state: Record, - extra?: Partial, -): { text: Component; stop(): void } { - const text = renderBashCall( - args, - createPassThroughTheme(), - makeContext({ - executionStarted: true, - isPartial: true, - state, - ...extra, - }), - ); - return { - text, - stop: () => { - renderBashCall( - args, - createPassThroughTheme(), - makeContext({ - executionStarted: true, - isPartial: false, - state, - lastComponent: text, - ...extra, - }), - ); - }, - }; -} - // ─── Args Shapes ───────────────────────────────────────────────────────────── test("renderBashCall uses ellipsis when command is missing", () => { @@ -131,7 +89,7 @@ test("renderBashCall previews long commands by visual line", () => { ); const lines = text.render(60); assert.equal(lines.length, 3); - assert.match(lines[2] ?? "", /more visual lines.*Ctrl\+O to expand/); + assert.match(lines[2] ?? "", /more visual lines?.*Ctrl\+O to expand/); }); test("renderBashCall expands the complete command", () => { @@ -219,7 +177,7 @@ test("renderBashCall applies ANSI color to command with ANSI theme", () => { // ─── Context States ────────────────────────────────────────────────────────── -test("renderBashCall no spinner when executionStarted is false", () => { +test("renderBashCall renders command text regardless of executionStarted/isPartial", () => { const text = renderBashCall( { command: "npm test" }, createPassThroughTheme(), @@ -228,100 +186,15 @@ test("renderBashCall no spinner when executionStarted is false", () => { assert.equal(renderedText(text), "$ npm test"); }); -test("renderBashCall no spinner when isPartial is false even if executionStarted", () => { +test("renderBashCall renders command text when executionStarted and isPartial are true", () => { const text = renderBashCall( { command: "npm test" }, createPassThroughTheme(), - makeContext({ executionStarted: true, isPartial: false }), + makeContext({ executionStarted: true, isPartial: true }), ); assert.equal(renderedText(text), "$ npm test"); }); -test("renderBashCall shows spinner when executionStarted and isPartial are true", () => { - const state: Record = {}; - const { text, stop } = createSpinningBashCall({ command: "npm test" }, state); - try { - const rendered = renderedText(text); - assert.match(rendered, /^⠋ \$ npm test · 0s$/); - } finally { - stop(); - } -}); - -test("renderBashCall spinner animates frame index over time via setInterval", async () => { - const state: Record = {}; - let invalidateCount = 0; - const { text, stop } = createSpinningBashCall( - { command: "npm test" }, - state, - { invalidate: () => { invalidateCount++; } }, - ); - try { - const frame0 = renderedText(text); - assert.match(frame0, /^⠋/); - - // Wait for at least one interval tick (200ms interval) - await new Promise((r) => setTimeout(r, 250)); - - const frame1 = renderedText(text); - assert.notEqual(frame1, frame0, "spinner frame should advance"); - assert.match(frame1, /^⠙/); - assert.ok(invalidateCount > 0, "invalidate should be called during animation"); - } finally { - stop(); - } -}); - -test("renderBashCall stops spinner and clears timer when shouldSpin becomes false", async () => { - const state: Record = {}; - const { text, stop } = createSpinningBashCall({ command: "npm test" }, state); - - assert.match(renderedText(text), /^⠋/); - - // Transition to complete state via stop - stop(); - assert.equal(renderedText(text), "$ npm test"); - - // Wait to verify no further animation - await new Promise((r) => setTimeout(r, 100)); - assert.equal(renderedText(text), "$ npm test", "no spinner after completion"); -}); - -test("renderBashCall reuses existing spinner state from context.state", () => { - const state: Record = {}; - const { text: text1, stop } = createSpinningBashCall({ command: "npm test" }, state); - assert.ok(state[BASH_SPINNER_STATE_KEY] !== undefined, "state should have spinner state"); - - // Second call with same state - const text2 = renderBashCall( - { command: "npm test" }, - createPassThroughTheme(), - makeContext({ - executionStarted: true, - isPartial: true, - state, - lastComponent: text1, - }), - ); - const state2 = state[BASH_SPINNER_STATE_KEY] as Record; - assert.ok(state2 !== undefined); - assert.ok(state2.frameIndex !== undefined); - stop(); -}); - -test("renderBashCall stores state under __piToolDisplayBashSpinner key", () => { - const state: Record = {}; - const { stop } = createSpinningBashCall({ command: "npm test" }, state); - try { - assert.ok(state[BASH_SPINNER_STATE_KEY] !== undefined); - const spinnerState = state[BASH_SPINNER_STATE_KEY] as Record; - assert.equal(typeof spinnerState.frameIndex, "number"); - assert.equal(typeof spinnerState.startedAt, "number"); - } finally { - stop(); - } -}); - test("renderBashCall does not create spinner state when state is null/undefined", () => { const text = renderBashCall( { command: "npm test" }, @@ -329,141 +202,76 @@ test("renderBashCall does not create spinner state when state is null/undefined" makeContext({ executionStarted: true, isPartial: true, - state: undefined, }), ); assert.equal(renderedText(text), "$ npm test"); }); -test("renderBashCall does not create spinner state when state is a primitive", () => { - const text = renderBashCall( +// ─── lastComponent Preservation ────────────────────────────────────────────── + +test("renderBashCall preserves the same component reference via lastComponent", () => { + const first = renderBashCall( { command: "npm test" }, createPassThroughTheme(), - makeContext({ - executionStarted: true, - isPartial: true, - state: "string-state", - }), + makeContext({ executionStarted: true, isPartial: true }), ); - assert.equal(renderedText(text), "$ npm test"); -}); -test("renderBashCall creates spinner state when state is an array (arrays are objects)", () => { - const stateArray: unknown[] = []; - const text = renderBashCall( + const second = renderBashCall( { command: "npm test" }, createPassThroughTheme(), makeContext({ executionStarted: true, - isPartial: true, - state: stateArray, + isPartial: false, + lastComponent: first, }), ); - // Arrays are objects in JS, so the implementation treats them as valid state carriers - assert.match(renderedText(text), /^⠋/); - // Clean up the interval set on the array - const spyState = (stateArray as unknown as Record)[BASH_SPINNER_STATE_KEY] as Record | undefined; - if (spyState?.timer) { - clearInterval(spyState.timer as ReturnType); - } -}); - -// ─── Elapsed Time Formatting ───────────────────────────────────────────────── -test("renderBashCall shows elapsed seconds when spinning and under 60s", () => { - const state: Record = {}; - state[BASH_SPINNER_STATE_KEY] = { frameIndex: 0, startedAt: Date.now() }; - const { text, stop } = createSpinningBashCall({ command: "npm test" }, state); - try { - assert.match(renderedText(text), /· \d+s$/); - } finally { - stop(); - } + assert.equal(first, second, "should return the same component instance via lastComponent"); }); -// ─── Multiple Concurrent Bash Calls ────────────────────────────────────────── +// ─── No Spinner Animation ──────────────────────────────────────────────────── -test("multiple concurrent bash calls have independent spinner states", () => { - const stateA: Record = {}; - const stateB: Record = {}; +test("renderBashCall does not use setInterval or invalidate for animation", () => { + const originalSetInterval = globalThis.setInterval; + let intervalCreated = false; + globalThis.setInterval = ((fn: (...args: unknown[]) => unknown, ms?: number, ..._args: unknown[]) => { + intervalCreated = true; + return originalSetInterval(fn, ms ?? 0); + }) as typeof globalThis.setInterval; - const { text: textA, stop: stopA } = createSpinningBashCall({ command: "npm test" }, stateA); - const { text: textB, stop: stopB } = createSpinningBashCall({ command: "npm build" }, stateB); try { - const aKey = stateA[BASH_SPINNER_STATE_KEY] as Record; - const bKey = stateB[BASH_SPINNER_STATE_KEY] as Record; - assert.ok(aKey !== bKey, "each bash call gets its own spinner state object"); - - // Each renders independently with its own command - assert.match(renderedText(textA), /npm test/); - assert.match(renderedText(textB), /npm build/); - - // Complete stateA independently (spinner stops for A) - stopA(); - assert.equal(renderedText(textA), "$ npm test", "A spinner stopped after stopA"); - - // stateB should still show spinner prefix (frame may not have advanced yet) - assert.match(renderedText(textB), /^⠋/, "B should still show spinner after A completed"); - } finally { - stopA(); - stopB(); - } -}); - -// ─── Rapid Invalidate / Repeated Calls ─────────────────────────────────────── - -test("renderBashCall handles repeated calls without creating multiple timers", () => { - const state: Record = {}; - let invalidateCount = 0; - - const { text, stop } = createSpinningBashCall( - { command: "npm test" }, - state, - { invalidate: () => { invalidateCount++; } }, - ); - - // Call renderBashCall repeatedly with same state (simulating rapid re-renders) - for (let i = 0; i < 5; i++) { - renderBashCall( + const text = renderBashCall( { command: "npm test" }, createPassThroughTheme(), - makeContext({ - executionStarted: true, - isPartial: true, - state, - lastComponent: text, - invalidate: () => { invalidateCount++; }, - }), + makeContext({ executionStarted: true, isPartial: true }), ); + assert.equal(renderedText(text), "$ npm test"); + assert.equal(intervalCreated, false, "no setInterval should be created"); + } finally { + globalThis.setInterval = originalSetInterval; } - - const spinnerState = state[BASH_SPINNER_STATE_KEY] as Record; - // Only one timer should be active (timer is only set if !spinnerState.timer) - const timerCount = spinnerState.timer !== undefined ? 1 : 0; - assert.equal(timerCount, 1, "multiple renderBashCall calls should not create multiple timers"); - - stop(); }); -// ─── lastComponent Preservation ────────────────────────────────────────────── - -test("renderBashCall preserves the same component reference", () => { - const initialState: Record = {}; - const { text: first, stop } = createSpinningBashCall({ command: "npm test" }, initialState); - - const second = renderBashCall( +test("renderBashCall renders deterministic output for partial execution", () => { + const text = renderBashCall( { command: "npm test" }, createPassThroughTheme(), - makeContext({ - executionStarted: true, - isPartial: false, - state: initialState, - lastComponent: first, - }), + makeContext({ executionStarted: true, isPartial: true }), ); + // No spinner frame, no elapsed time — just the command + assert.equal(renderedText(text), "$ npm test"); +}); - assert.equal(first, second, "should return the same component instance via lastComponent"); - stop(); +test("renderBashCall does not show elapsed time or spinner frames", () => { + const text = renderBashCall( + { command: "npm test", timeout: 30 }, + createPassThroughTheme(), + makeContext({ executionStarted: true, isPartial: true }), + ); + const rendered = renderedText(text); + assert.doesNotMatch(rendered, /^⠋/, "no spinner frame"); + assert.doesNotMatch(rendered, /· \d+s$/, "no elapsed time"); + assert.equal(rendered, "$ npm test (timeout 30s)"); }); // ─── Edge Cases ────────────────────────────────────────────────────────────── diff --git a/tests/config-modal.test.ts b/tests/config-modal.test.ts index d774904..16349a7 100644 --- a/tests/config-modal.test.ts +++ b/tests/config-modal.test.ts @@ -1,8 +1,10 @@ import assert from "node:assert/strict"; import test from "node:test"; import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; -import { applySetting, buildInspectorSettings } from "../src/config-modal.ts"; -import { registerToolDisplayCommand } from "../src/config-command.ts"; +import { applySetting, buildInspectorSettings, createSettingMutation } from "../src/config-modal.ts"; +import { registerToolDisplayCommand, type ToolDisplayConfigMutation } from "../src/config-command.ts"; +import { mergeProjectConfig } from "../src/config-store.ts"; +import { getToolDisplayPresetConfig } from "../src/presets.ts"; import { DEFAULT_TOOL_DISPLAY_CONFIG, type ToolDisplayConfig, @@ -62,10 +64,10 @@ function createControllerStub( ): { controller: { getConfig: () => ToolDisplayConfig; - setConfig: (next: ToolDisplayConfig, ctx: ExtensionCommandContext) => void; + mutateConfig: (mutation: ToolDisplayConfigMutation, ctx: ExtensionCommandContext) => void; getCapabilities: () => ToolDisplayCapabilities; }; - getLastSet: () => { config: ToolDisplayConfig | null; ctx: ExtensionCommandContext | null }; + getLastSet: () => { config: ToolDisplayConfig | null; mutation: ToolDisplayConfigMutation | null; ctx: ExtensionCommandContext | null }; } { let config: ToolDisplayConfig = { ...DEFAULT_TOOL_DISPLAY_CONFIG, @@ -74,7 +76,11 @@ function createControllerStub( ...(initialConfig?.builtInToolDisplays ?? DEFAULT_TOOL_DISPLAY_CONFIG.builtInToolDisplays), }, }; - const last = { config: null as ToolDisplayConfig | null, ctx: null as ExtensionCommandContext | null }; + const last = { + config: null as ToolDisplayConfig | null, + mutation: null as ToolDisplayConfigMutation | null, + ctx: null as ExtensionCommandContext | null, + }; return { controller: { @@ -82,9 +88,12 @@ function createControllerStub( ...config, builtInToolDisplays: { ...config.builtInToolDisplays }, }), - setConfig: (next: ToolDisplayConfig, ctx: ExtensionCommandContext) => { - config = { ...next, builtInToolDisplays: { ...next.builtInToolDisplays } }; + mutateConfig: (mutation: ToolDisplayConfigMutation, ctx: ExtensionCommandContext) => { + config = mutation.type === "patch" + ? mergeProjectConfig(config, mutation.patch) + : getToolDisplayPresetConfig(mutation.type === "preset" ? mutation.preset : "opencode"); last.config = config; + last.mutation = mutation; last.ctx = ctx; }, getCapabilities: () => capabilities ?? { hasRtkOptimizer: false }, @@ -106,6 +115,22 @@ test("registerToolDisplayCommand registers a handler for 'tool-display'", () => assert.ok(getHandler(), "expected handler to be registered"); }); +test("settings and direct commands submit explicit patch, preset, and reset intents", async () => { + assert.deepEqual( + createSettingMutation(DEFAULT_TOOL_DISPLAY_CONFIG, "searchOutputMode", "count"), + { type: "patch", patch: { searchOutputMode: "count" } }, + ); + + const { api, getHandler } = createPiStub(); + const { controller, getLastSet } = createControllerStub(); + const { ctx } = createCtxStub(true); + registerToolDisplayCommand(api, controller); + await getHandler()!("preset balanced", ctx); + assert.deepEqual(getLastSet().mutation, { type: "preset", preset: "balanced" }); + await getHandler()!("reset", ctx); + assert.deepEqual(getLastSet().mutation, { type: "reset" }); +}); + test("'show' argument notifies with config summary", async () => { const { api, getHandler } = createPiStub(); const { controller } = createControllerStub({}, { hasRtkOptimizer: true }); diff --git a/tests/config-store.test.ts b/tests/config-store.test.ts index 1a945d3..f211371 100644 --- a/tests/config-store.test.ts +++ b/tests/config-store.test.ts @@ -4,7 +4,9 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; import { + applyToolDisplayConfigPatch, loadToolDisplayConfig, + mergeProjectConfig, normalizeToolDisplayConfig, saveToolDisplayConfig, } from "../src/config-store.ts"; @@ -107,6 +109,65 @@ test("config load reports parse errors and falls back to defaults", () => { }); }); +test("project overlays merge built-in and custom-tool fields without resetting global siblings", () => { + const globalConfig = normalizeToolDisplayConfig({ + builtInToolDisplays: { read: true, bash: false }, + customToolOverrides: { + custom_search: { + enabled: false, + kind: "mcp", + outputMode: "summary", + overrideCallRenderer: true, + }, + }, + }); + + const effective = mergeProjectConfig(globalConfig, { + builtInToolDisplays: { read: false }, + customToolOverrides: { + custom_search: { outputMode: "preview" }, + }, + }); + + assert.equal(effective.builtInToolDisplays.read, false); + assert.equal(effective.builtInToolDisplays.bash, false); + assert.deepEqual(effective.customToolOverrides.custom_search, { + enabled: false, + kind: "mcp", + outputMode: "preview", + overrideCallRenderer: true, + }); +}); + +test("an explicit global patch cannot persist fields inherited from a project overlay", () => { + const globalConfig = normalizeToolDisplayConfig({ + readOutputMode: "hidden", + searchOutputMode: "hidden", + customToolOverrides: { + custom_search: { enabled: true, kind: "mcp", outputMode: "summary", overrideCallRenderer: true }, + }, + }); + const overlay = { + readOutputMode: "preview" as const, + customToolOverrides: { custom_search: { outputMode: "preview" as const } }, + }; + + const persisted = applyToolDisplayConfigPatch(globalConfig, { searchOutputMode: "count" }); + const effective = mergeProjectConfig(persisted, overlay); + + assert.equal(persisted.readOutputMode, "hidden"); + assert.equal(persisted.searchOutputMode, "count"); + assert.deepEqual(persisted.customToolOverrides.custom_search, globalConfig.customToolOverrides.custom_search); + assert.equal(effective.readOutputMode, "preview"); + assert.equal(effective.searchOutputMode, "count"); + assert.deepEqual(effective.customToolOverrides.custom_search, { + enabled: true, + kind: "mcp", + outputMode: "preview", + overrideCallRenderer: true, + }); +}); + test("config save writes normalized JSON and cleans temporary file on failure", () => { withTempDir("pi-tool-display-config-save-", (dir) => { const configFile = join(dir, "config.json"); diff --git a/tests/index-integration.test.ts b/tests/index-integration.test.ts index e496136..f24c453 100644 --- a/tests/index-integration.test.ts +++ b/tests/index-integration.test.ts @@ -1,20 +1,27 @@ import assert from "node:assert/strict"; -import { mkdtempSync, rmSync } from "node:fs"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import test, { after } from "node:test"; -import type { - ExtensionAPI, - ExtensionCommandContext, +import { + CONFIG_DIR_NAME, + createReadTool, + initTheme, + ToolExecutionComponent, + type ExtensionAPI, + type ExtensionCommandContext, } from "@earendil-works/pi-coding-agent"; const testAgentDir = mkdtempSync(join(tmpdir(), "pi-tool-display-index-")); const originalAgentDir = process.env.PI_CODING_AGENT_DIR; process.env.PI_CODING_AGENT_DIR = testAgentDir; const { default: toolDisplayExtension } = await import("../src/index.ts"); +const { createRendererCatalog } = await import("../src/renderer-catalog.ts"); +const { DEFAULT_TOOL_DISPLAY_CONFIG } = await import("../src/types.ts"); if (originalAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR; else process.env.PI_CODING_AGENT_DIR = originalAgentDir; after(() => rmSync(testAgentDir, { recursive: true, force: true })); +initTheme(undefined, false); // --------------------------------------------------------------------------- // Helpers @@ -98,10 +105,13 @@ test("entry point registers expected lifecycle handlers", () => { assert.ok(beforeAgentStartCount >= 1, "at least one before_agent_start handler registered"); }); -test("entry point registers tool-display command", () => { - const { api, capturedCommands } = createApiStub(); +test("entry point registers tool-display command on session_start", async () => { + const { api, capturedCommands, capturedHandlers } = createApiStub(); toolDisplayExtension(api); + // Command registration now happens inside session_start + for (const { event, handler } of capturedHandlers) if (event === "session_start") await handler({}, { ui: { notify() {} }, cwd: process.cwd(), isProjectTrusted: () => false }); + const cmdNames = capturedCommands.map((c) => c.name); assert.ok(cmdNames.includes("tool-display"), "tool-display command registered"); }); @@ -111,7 +121,7 @@ test("entry point never registers tools across initialization, lifecycle, config toolDisplayExtension(api); assert.deepEqual(capturedTools, []); - const ctx = { hasUI: false, ui: { notify() {}, theme: { fg: (_c: string, text: string) => text } } } as unknown as ExtensionCommandContext; + const ctx = { hasUI: false, ui: { notify() {}, theme: { fg: (_c: string, text: string) => text } }, cwd: process.cwd(), isProjectTrusted: () => false } as unknown as ExtensionCommandContext; for (const event of ["session_start", "before_agent_start", "before_agent_start", "session_shutdown"]) { for (const captured of capturedHandlers.filter((entry) => entry.event === event)) { await captured.handler(event === "session_shutdown" ? { reason: "reload" } : {}, ctx); @@ -137,6 +147,8 @@ test("session_start handler refreshes capabilities and notifies pending errors", theme: { fg: (_c: string, t: string) => t }, notify: (_msg: string, _level: string) => { /* no-op */ }, }, + cwd: process.cwd(), + isProjectTrusted: () => false, }; // Should not throw @@ -160,11 +172,10 @@ test("multiple calls to toolDisplayExtension are idempotent", async () => { // Call twice toolDisplayExtension(api); toolDisplayExtension(api); - for (const { event, handler } of capturedHandlers) if (event === "before_agent_start") await handler(); + // Registration now happens inside session_start + const sessionCtx = { ui: { theme: {}, notify() {} }, cwd: process.cwd(), isProjectTrusted: () => false }; + for (const { event, handler } of capturedHandlers) if (event === "session_start") await handler({}, sessionCtx); - // Second call should not throw. Tools may be registered again (that's up - // to the extension loader to deduplicate), but the extension itself must - // not crash. const toolNames = capturedTools.map((t) => t.name); assert.equal(toolNames.some((name) => ["read", "grep", "find", "ls", "edit", "write"].includes(name)), false); assert.equal(toolNames.length, 0); @@ -197,7 +208,6 @@ test("entry point capability discovery tolerates source metadata", () => { }); test("graceful degradation: extension throws when registerCommand is missing", () => { - // Simulate a minimal stub missing registerCommand const minimalApi = { registerTool(): void { /* no-op */ }, on(): void { /* no-op */ }, @@ -205,12 +215,10 @@ test("graceful degradation: extension throws when registerCommand is missing", ( getCommands(): Array<{ name: string }> { return []; }, } as unknown as ExtensionAPI; - // registerToolDisplayCommand calls pi.registerCommand directly, so this - // is expected to throw in a peer-dep mismatch scenario. assert.throws( () => toolDisplayExtension(minimalApi), /registerCommand/i, - "missing registerCommand should propagate", + "missing registerCommand should propagate during extension registration", ); }); @@ -246,7 +254,7 @@ test("lifecycle events fire in expected order during a session lifecycle", async await beforeHandler(); await sessionHandler( {}, - { ui: { theme: { fg: (_c: string, t: string) => t }, notify: () => {} } }, + { ui: { theme: { fg: (_c: string, t: string) => t }, notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }, ); // All handlers executed without throwing - this is the main assertion @@ -261,7 +269,7 @@ test("session_start handler tolerates missing ctx.ui", async () => { assert.ok(sessionHandler); // ctx with no ui (edge case from older pi versions) - await assert.doesNotReject(async () => sessionHandler({}, {})); + await assert.doesNotReject(async () => sessionHandler({}, { cwd: process.cwd(), isProjectTrusted: () => false })); }); test("before_agent_start handler tolerates being called multiple times", async () => { @@ -283,16 +291,113 @@ test("session_start handler tolerates being called multiple times", async () => const sessionHandler = capturedHandlers.find((h) => h.event === "session_start")?.handler; assert.ok(sessionHandler); - const ctx = { ui: { theme: {}, notify: () => {} } }; + const ctx = { ui: { theme: {}, notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }; await assert.doesNotReject(async () => sessionHandler({}, ctx)); await assert.doesNotReject(async () => sessionHandler({}, ctx)); await assert.doesNotReject(async () => sessionHandler({}, ctx)); }); +test("session transition and disabled config restore native renderer ownership", async () => { + const configFile = join(testAgentDir, "extensions", "pi-tool-display", "config.json"); + mkdirSync(join(testAgentDir, "extensions", "pi-tool-display"), { recursive: true }); + writeFileSync(configFile, JSON.stringify({ enabled: true }), "utf8"); + + const prototype = ToolExecutionComponent.prototype as unknown as { getCallRenderer: Function }; + const { api, capturedHandlers } = createApiStub(); + toolDisplayExtension(api); + const sessionStart = capturedHandlers.find(({ event }) => event === "session_start")?.handler; + const sessionShutdown = (reason: string) => { + for (const { event, handler } of capturedHandlers) if (event === "session_shutdown") handler({ reason }); + }; + const ctx = { ui: { theme: {}, notify() {} }, cwd: process.cwd(), isProjectTrusted: () => false }; + + sessionShutdown("new"); + const nativeCall = prototype.getCallRenderer; + await sessionStart?.({}, ctx); + assert.notStrictEqual(prototype.getCallRenderer, nativeCall); + sessionShutdown("new"); + assert.strictEqual(prototype.getCallRenderer, nativeCall); + + writeFileSync(configFile, JSON.stringify({ enabled: false }), "utf8"); + await sessionStart?.({}, ctx); + assert.strictEqual(prototype.getCallRenderer, nativeCall); +}); + +test("reload preserves the trusted project overlay before another session_start", async () => { + const globalConfigFile = join(testAgentDir, "extensions", "pi-tool-display", "config.json"); + mkdirSync(join(testAgentDir, "extensions", "pi-tool-display"), { recursive: true }); + writeFileSync(globalConfigFile, JSON.stringify({ readOutputMode: "hidden" }), "utf8"); + const projectDir = join(testAgentDir, "trusted-project"); + const projectConfigDir = join(projectDir, CONFIG_DIR_NAME, "extensions", "pi-tool-display"); + mkdirSync(projectConfigDir, { recursive: true }); + writeFileSync(join(projectConfigDir, "config.json"), JSON.stringify({ readOutputMode: "preview" }), "utf8"); + + const first = createApiStub(); + toolDisplayExtension(first.api); + const ctx = { ui: { theme: {}, notify() {} }, cwd: projectDir, isProjectTrusted: () => true }; + await first.capturedHandlers.find(({ event }) => event === "session_start")?.handler({}, ctx); + for (const { event, handler } of first.capturedHandlers) if (event === "session_shutdown") handler({ reason: "reload" }); + + const second = createApiStub(); + toolDisplayExtension(second.api); + const notifications: string[] = []; + await second.capturedCommands.find(({ name }) => name === "tool-display")?.handler?.("show", { + ui: { notify(message: string) { notifications.push(message); } }, + }); + assert.match(notifications.join("\n"), /read=preview/); + for (const { event, handler } of second.capturedHandlers) if (event === "session_shutdown") handler({ reason: "quit" }); +}); + +test("public config mutation invalidates ToolExecution rows already rendered", async () => { + const runtime = createApiStub(); + toolDisplayExtension(runtime.api); + const row = new ToolExecutionComponent( + "read", + "config-refresh", + { path: "fixture.txt" }, + {}, + createReadTool(process.cwd()), + { requestRender() {} } as any, + process.cwd(), + ); + row.updateResult({ content: [{ type: "text", text: "one\ntwo" }], details: {} } as any); + row.render(80); + const originalInvalidate = row.invalidate.bind(row); + let invalidations = 0; + row.invalidate = () => { invalidations++; originalInvalidate(); }; + + const command = runtime.capturedCommands.find(({ name }) => name === "tool-display")?.handler; + await command?.("preset balanced", { ui: { notify() {} } }); + assert.ok(invalidations > 0); + row.render(80); + const afterFirstMutation = invalidations; + await command?.("preset verbose", { ui: { notify() {} } }); + assert.ok(invalidations > afterFirstMutation); + for (const { event, handler } of runtime.capturedHandlers) if (event === "session_shutdown") handler({ reason: "quit" }); +}); + +test("ordinary session factory replacement preserves producer Adapter intent", () => { + const first = createApiStub(); + toolDisplayExtension(first.api); + const apiSymbol = Symbol.for("pi-tool-display.api.v1"); + const producerApi = (globalThis as any)[apiSymbol]; + producerApi.registerAdapter({ id: "before-transition", toolName: "before_transition", kind: "generic" }); + for (const { event, handler } of first.capturedHandlers) if (event === "session_shutdown") handler({ reason: "new" }); + producerApi.registerAdapter({ id: "during-transition", toolName: "during_transition", kind: "generic" }); + + const second = createApiStub(); + toolDisplayExtension(second.api); + assert.strictEqual((globalThis as any)[apiSymbol], producerApi); + const catalog = createRendererCatalog(); + assert.ok(catalog.resolve({ toolName: "before_transition", arguments: {} }, DEFAULT_TOOL_DISPLAY_CONFIG, {})); + assert.ok(catalog.resolve({ toolName: "during_transition", arguments: {} }, DEFAULT_TOOL_DISPLAY_CONFIG, {})); + for (const { event, handler } of second.capturedHandlers) if (event === "session_shutdown") handler({ reason: "quit" }); +}); + test("display policy installs without registering executable definitions", async () => { const { api, capturedTools, capturedHandlers } = createApiStub(); toolDisplayExtension(api); - for (const { event, handler } of capturedHandlers) if (event === "session_start") await handler({}, { ui: { notify: () => {} } }); + for (const { event, handler } of capturedHandlers) if (event === "session_start") await handler({}, { ui: { notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }); assert.equal(capturedTools.length, 0); for (const tool of capturedTools) { diff --git a/tests/pure-bash-display.test.ts b/tests/pure-bash-display.test.ts index f001dfe..9d8c6ed 100644 --- a/tests/pure-bash-display.test.ts +++ b/tests/pure-bash-display.test.ts @@ -24,14 +24,14 @@ test("Bash resolves call and independently folded success/error results from ori assert.notStrictEqual(plan.result, nativeResult); assert.match(render(plan.call!({ command: "echo " + "word ".repeat(30) }, theme, { executionStarted: false, isPartial: false }), 30), /more visual lines/); assert.match(render(plan.result!(output("alpha\nbeta\ngamma"), { expanded: false, isPartial: false }, theme, { args: { command: "echo ok" } })), /3 lines returned/); - assert.match(render(plan.result!(output("failure detail that wraps across the terminal width"), { expanded: false, isPartial: false }, theme, { args: { command: "false" }, isError: true }), 20), /command failed[\s\S]*more visua/); + assert.match(render(plan.result!(output("failure detail that wraps across the terminal width"), { expanded: false, isPartial: false }, theme, { args: { command: "false" }, isError: true }), 20), /command failed[\s\S]*(?:more visua|… \(\+\d+\))/); }); test("Bash partial, empty, truncation, and expanded output retain presentation behavior", () => { const plan = resolver({ bashOutputMode: "preview", previewLines: 1, showTruncationHints: true }) .resolve({ toolName: "bash", arguments: { command: "printf x" }, builtIn: true }, {}); assert.equal(render(plan.result!(output(""), { expanded: false, isPartial: true }, theme, {})), ""); - assert.match(render(plan.result!(output("first\nsecond"), { expanded: false, isPartial: true }, theme, {})), /first[\s\S]*more line/); + assert.match(render(plan.result!(output("first\nsecond"), { expanded: false, isPartial: true }, theme, {})), /first[\s\S]*more visual/); assert.match(render(plan.result!(output("", { truncation: { truncated: true }, fullOutputPath: "/tmp/full" }), { expanded: false, isPartial: false }, theme, { args: { command: "true" } })), /no output[\s\S]*truncated[\s\S]*full output/); assert.match(render(plan.result!(output("first\nsecond"), { expanded: true, isPartial: false }, theme, {})), /second/); }); @@ -48,9 +48,9 @@ test("Bash Host Adapter changes only renderer selection and does not stack on re const row = { toolName: "bash", args: { command: "echo ok" }, toolDefinition: definition, builtInToolDefinition: definition }; const pristine = Object.getOwnPropertyDescriptors(definition); - const first = installPiHostAdapter(host, resolver(), "0.80.3"); + const first = installPiHostAdapter(host, resolver(), "0.81.1"); const patchedCall = host.getCallRenderer; - const second = installPiHostAdapter(host, resolver(), "0.80.3"); + const second = installPiHostAdapter(host, resolver(), "0.81.1"); assert.strictEqual(host.getCallRenderer, patchedCall); assert.match(render(host.getCallRenderer.call(row)!(row.args, theme, { executionStarted: false, isPartial: false })), /echo ok/); assert.deepEqual(Object.getOwnPropertyDescriptors(definition), pristine); @@ -59,3 +59,31 @@ test("Bash Host Adapter changes only renderer selection and does not stack on re assert.strictEqual(host.getResultRenderer, originalResult); first.dispose(); }); + +test("Visual line preview limits long single-line output by visual rows, not logical lines", () => { + const resolver = createToolDisplayResolver( + () => ({ + ...DEFAULT_TOOL_DISPLAY_CONFIG, + builtInToolDisplays: { ...DEFAULT_TOOL_DISPLAY_CONFIG.builtInToolDisplays, bash: true }, + bashOutputMode: "opencode", + bashCollapsedLines: 2, + }), + createRendererCatalog(), + ); + const plan = resolver.resolve( + { toolName: "bash", arguments: { command: "echo test" }, builtIn: true }, + {}, + ); + // 400-char single line at width 40 wraps to ~10 visual rows + const longLine = "A".repeat(400); + const result = plan.result!( + { content: [{ type: "text", text: longLine }], details: {} }, + { expanded: false, isPartial: false }, + theme, + { args: { command: "echo test" } }, + ); + const rows = result.render(40); + // Should be capped at 2 (collapsedLines) + 1 (hint line) = 3 + assert.ok(rows.length <= 3, `expected <= 3 rows but got ${rows.length}`); + assert.match(rows[rows.length - 1], /more visual lines/); +}); diff --git a/tests/pure-edit-display.test.ts b/tests/pure-edit-display.test.ts index ddc8384..8c227aa 100644 --- a/tests/pure-edit-display.test.ts +++ b/tests/pure-edit-display.test.ts @@ -47,10 +47,10 @@ test("pending edit diff honors the collapsed row state", () => { const collapsed = render(selected.call!(args, theme, { isPartial: true, argsComplete: true, expanded: false })); const expanded = render(selected.call!(args, theme, { isPartial: true, argsComplete: true, expanded: true })); - assert.match(collapsed, /more diff lines/); + assert.match(collapsed, /more visual diff lines/); assert.doesNotMatch(collapsed, /old 29|new 29/); const plainExpanded = expanded.replace(/\x1b\[[0-9;]*m/g, ""); - assert.doesNotMatch(plainExpanded, /more diff lines/); + assert.doesNotMatch(plainExpanded, /more visual diff lines/); assert.match(plainExpanded, /old 29/); assert.match(plainExpanded, /new 29/); }); diff --git a/tests/pure-grep-display.test.ts b/tests/pure-grep-display.test.ts index 252cdc7..085632f 100644 --- a/tests/pure-grep-display.test.ts +++ b/tests/pure-grep-display.test.ts @@ -3,7 +3,7 @@ import test from "node:test"; import { DEFAULT_TOOL_DISPLAY_CONFIG } from "../src/types.ts"; import { createToolDisplayResolver } from "../src/tool-display-resolver.ts"; import { createRendererCatalog } from "../src/renderer-catalog.ts"; -import { installPiHostAdapter } from "../src/pi-host-adapter.ts"; +import { installPiHostAdapter, invalidatePiHostAdapterRows } from "../src/pi-host-adapter.ts"; const theme = { fg: (_color: string, text: string) => text, bold: (text: string) => text }; const output = { content: [{ type: "text", text: "a:1\nb:2\nc:3" }], details: {} }; @@ -65,7 +65,7 @@ test("Pi Host Adapter is transactional, idempotent, receiver-safe, and conflict- const pristine = Object.getOwnPropertyDescriptors(host); let installation: ReturnType | undefined; try { - installation = installPiHostAdapter(host, config("count"), "0.80.3"); + installation = installPiHostAdapter(host, config("count"), "0.81.1"); assert.equal(installation.installed, true); const installed = Object.getOwnPropertyDescriptors(host); assert.notStrictEqual(installed.getCallRenderer.value, pristine.getCallRenderer.value); @@ -84,7 +84,7 @@ test("Pi Host Adapter is transactional, idempotent, receiver-safe, and conflict- assert.strictEqual(callRenderer.apply(rendererReceiver, [1]).receiver, rendererReceiver); assert.strictEqual(resultRenderer.apply(rendererReceiver, [2]).receiver, rendererReceiver); - const second = installPiHostAdapter(host, config("preview"), "0.80.3"); + const second = installPiHostAdapter(host, config("preview"), "0.81.1"); assert.equal(second.installed, true); assert.strictEqual(host.getResultRenderer, installed.getResultRenderer.value); const grepRow = { toolName: "grep", args: { pattern: "x" }, builtInToolDefinition: { name: "grep" } }; @@ -105,6 +105,52 @@ test("Pi Host Adapter is transactional, idempotent, receiver-safe, and conflict- } }); +test("Pi Host Adapter invalidates rows already resolved through the display seam", () => { + const { host } = syntheticHost(); + const installation = installPiHostAdapter(host, config("count"), "0.81.1"); + let invalidations = 0; + const row = { toolName: "grep", args: {}, builtInToolDefinition: { name: "grep" }, invalidate() { invalidations++; } }; + host.getResultRenderer.call(row); + + invalidatePiHostAdapterRows(host); + assert.equal(invalidations, 1); + installation.dispose(); + invalidatePiHostAdapterRows(host); + assert.equal(invalidations, 1); +}); + +test("Pi Host Adapter disposal deactivates its wrapper beneath a later foreign wrapper", () => { + const { host } = syntheticHost(); + const installation = installPiHostAdapter(host, config("count"), "0.81.1"); + const installedSelector = host.getResultRenderer; + host.getResultRenderer = function (this: unknown, ...args: unknown[]) { + return installedSelector.apply(this, args); + }; + let invalidations = 0; + const row = { toolName: "grep", args: { pattern: "x" }, builtInToolDefinition: { name: "grep" }, invalidate() { invalidations++; } }; + + assert.match(render(host.getResultRenderer.call(row)(output, { expanded: false, isPartial: false }, theme)), /3 matches/); + installation.dispose(); + assert.equal(invalidations, 0); + assert.equal(render(host.getResultRenderer.call(row)(output, { expanded: false, isPartial: false }, theme)), "native result"); + invalidatePiHostAdapterRows(host); + assert.equal(invalidations, 0); +}); + +test("Pi Host Adapter disposers affect only their own resolver attachment", () => { + const { host } = syntheticHost(); + const first = installPiHostAdapter(host, config("count"), "0.81.1"); + const second = installPiHostAdapter(host, config("preview"), "0.81.1"); + const patchedResult = host.getResultRenderer; + + first.dispose(); + assert.strictEqual(host.getResultRenderer, patchedResult); + assert.match(render(host.getResultRenderer.call({ toolName: "grep", args: { pattern: "x" }, builtInToolDefinition: { name: "grep" } })(output, { expanded: false, isPartial: false }, theme)), /a:1/); + + second.dispose(); + assert.notStrictEqual(host.getResultRenderer, patchedResult); +}); + test("Pi Host Adapter retains ownership tracking until an interrupted mixed disposal can finish", () => { const target = syntheticHost().host; const pristineCall = Object.getOwnPropertyDescriptor(target, "getCallRenderer")!; @@ -115,7 +161,7 @@ test("Pi Host Adapter retains ownership tracking until an interrupted mixed disp return Reflect.defineProperty(object, property, descriptor); }, }); - const installation = installPiHostAdapter(host, config("count"), "0.80.3"); + const installation = installPiHostAdapter(host, config("count"), "0.81.1"); assert.equal(installation.installed, true); const patchedCall = target.getCallRenderer; const foreignResult = function () { return "foreign"; }; @@ -124,15 +170,15 @@ test("Pi Host Adapter retains ownership tracking until an interrupted mixed disp installation.dispose(); assert.strictEqual(target.getCallRenderer, patchedCall); assert.strictEqual(target.getResultRenderer, foreignResult); - assert.equal(installPiHostAdapter(host, config("count"), "0.80.3").installed, false); + assert.equal(installPiHostAdapter(host, config("count"), "0.81.1").installed, false); blockCallRestore = false; installation.dispose(); assert.deepEqual(Object.getOwnPropertyDescriptor(target, "getCallRenderer"), pristineCall); assert.strictEqual(target.getResultRenderer, foreignResult); }); -test("Pi Host Adapter accepts stable Pi versions from 0.81.1 onward", () => { - for (const version of ["0.81.1", "0.82.0", "1.0.0"]) { +test("Pi Host Adapter accepts Pi 0.81.1 and later stable versions", () => { + for (const version of ["0.81.1", "0.82.0", "0.83.7", "1.0.0"]) { const { host } = syntheticHost(); const installation = installPiHostAdapter(host, config("count"), version); assert.equal(installation.installed, true); @@ -142,17 +188,17 @@ test("Pi Host Adapter accepts stable Pi versions from 0.81.1 onward", () => { test("Pi Host Adapter rejects unsupported and non-extensible hosts without descriptor changes", () => { for (const [host, version] of [ - [Object.preventExtensions(syntheticHost().host), "0.80.3"], + [Object.preventExtensions(syntheticHost().host), "0.81.1"], [syntheticHost().host, "0.81.0"], [syntheticHost().host, "0.82.0-beta.1"], - [{ getCallRenderer() {} }, "0.80.3"], + [{ getCallRenderer() {} }, "0.81.1"], ] as const) { const before = Object.getOwnPropertyDescriptors(host); assert.equal(installPiHostAdapter(host, config("count"), version).installed, false); assert.deepEqual(Object.getOwnPropertyDescriptors(host), before); } const hostile = new Proxy({}, { getOwnPropertyDescriptor() { throw new Error("hostile shape"); } }); - assert.equal(installPiHostAdapter(hostile, config("count"), "0.80.3").installed, false); + assert.equal(installPiHostAdapter(hostile, config("count"), "0.81.1").installed, false); }); test("Pi Host Adapter rolls back exact descriptors when a patch step fails", () => { @@ -168,6 +214,6 @@ test("Pi Host Adapter rolls back exact descriptors when a patch step fails", () return Reflect.defineProperty(object, property, descriptor); }, }); - assert.equal(installPiHostAdapter(host, config("count"), "0.80.3").installed, false); + assert.equal(installPiHostAdapter(host, config("count"), "0.81.1").installed, false); assert.deepEqual(Object.getOwnPropertyDescriptors(target), pristine); }); diff --git a/tests/real-runtime-contract.test.ts b/tests/real-runtime-contract.test.ts index ca32ae8..bad731a 100644 --- a/tests/real-runtime-contract.test.ts +++ b/tests/real-runtime-contract.test.ts @@ -67,20 +67,22 @@ const stableFixturePaths = (ownership: Array<{ name: string; sourceInfo: any }>) sourceInfo: { ...sourceInfo, path: sourceInfo.path?.replace(/pi-tool-display-contract-[^\\/]+/, "pi-tool-display-contract") }, })); -test("runtime matrix pins development, Pi 0.81.1, Pi 0.82.0, and the declared minimum", () => { +test("runtime matrix qualifies representative supported Pi versions", () => { assert.deepEqual(matrix.map(({ name, version }) => ({ name, version })), [ { name: "development", version: undefined }, { name: "pi-0.81.1", version: "0.81.1" }, { name: "pi-0.82.0", version: "0.82.0" }, - { name: "minimum-supported", version: "0.74.0" }, ]); assert.equal(matrix.every(({ required }) => required), true); assert.equal(matrix.find(({ name }) => name === "development")?.env, "PI_RUNTIME_DEV_ROOT"); + const packageJson = JSON.parse(readFileSync(resolve(process.cwd(), "package.json"), "utf8")); + assert.equal(packageJson.peerDependencies["@earendil-works/pi-coding-agent"], "*"); + assert.equal(packageJson.peerDependencies["@earendil-works/pi-tui"], "*"); }); for (const entry of matrix) { const runtimeRoot = process.env[entry.env]; - const optional = process.env.npm_lifecycle_event === "test:contract:local"; + const optional = process.env.npm_lifecycle_event !== "test:contract:required"; test(`real Pi runtime contract: ${entry.name}`, { skip: optional && !runtimeRoot ? `${entry.env} is not supplied` : false }, async () => { assert.ok(runtimeRoot, `${entry.env} is required (use npm run test:contract:local for optional local runtimes)`); const packagePath = runtimeRoot.endsWith("package.json") ? runtimeRoot : resolve(runtimeRoot.endsWith(".js") ? dirname(dirname(runtimeRoot)) : runtimeRoot, "package.json"); @@ -116,11 +118,15 @@ for (const entry of matrix) { ["mcp", "↳ 3 lines returned", "mcp proxy first line"], ["mcp_direct_fixture", "↳ 4 lines returned", "mcp direct first line"], ] as const; - for (const frame of [cold, plain(observation.present.tuiOutput.reload), plain(observation.present.tuiOutput.newCall)]) { + for (const [frameName, frame] of [ + ["cold", cold], + ["reload", plain(observation.present.tuiOutput.reload)], + ["new-call", plain(observation.present.tuiOutput.newCall)], + ] as const) { for (const [name, summary, nativeOutput] of summaries) { - assert.match(frame, new RegExp(`\\b${name}\\b`)); - assert.match(frame, new RegExp(`${summary}(?: • Ctrl\\+O to expand)?`), `${name} must render its exact line-count summary`); - assert.doesNotMatch(frame, new RegExp(nativeOutput)); + assert.match(frame, new RegExp(`\\b${name}\\b`), `${frameName}: ${name} call must remain visible`); + assert.match(frame, new RegExp(`${summary}(?: • Ctrl\\+O to expand)?`), `${frameName}: ${name} must render its exact line-count summary`); + assert.doesNotMatch(frame, new RegExp(nativeOutput), `${frameName}: ${name} native output must stay folded`); } } for (const frame of [observation.present.tuiOutput.expandedCold, observation.present.tuiOutput.expandedReload]) { @@ -309,8 +315,8 @@ for (const entry of matrix) { const partial = plain(bash.present.tuiOutput.partialNewCall); assert.match(partial, /contract streaming output/); assert.doesNotMatch(partial, /contract streaming folded second line/); - assert.notEqual(bash.present.tuiOutput.animatedPartialNewCall, ""); - assert.notEqual(plain(bash.present.tuiOutput.animatedPartialNewCall), partial); + // Spinner removed: no timer-driven animated frame is produced. + assert.equal(bash.present.tuiOutput.animatedPartialNewCall, ""); assert.match(plain(bash.present.tuiOutput.newCall), /contract final output/); assert.doesNotMatch(plain(bash.present.tuiOutput.newCall), /contract final folded second line/); assert.match(plain(bash.present.tuiOutput.expandedNewCall), /contract final folded second line/); @@ -321,7 +327,7 @@ for (const entry of matrix) { assert.doesNotMatch(plain(bash.present.tuiOutput.collapsedErrorNewCall), /contract error folded second line/); assert.deepEqual(bash.present.lifecycle, { reloads: 3, stableWrappers: true, wrappersAfterDispose: 0, descriptorsRestored: true, - timerBaseline: 0, timersWhilePartial: 1, timersAfterCompletion: 0, timersAfterDispose: 0, + timerBaseline: 0, timersWhilePartial: 0, timersAfterCompletion: 0, timersAfterDispose: 0, }); for (const frame of [bash.present.tuiOutput.expandedCold, bash.present.tuiOutput.expandedReload]) { assert.match(plain(frame), /contract success folded third line/); diff --git a/tests/reload-behavior.test.ts b/tests/reload-behavior.test.ts index 974480f..bab66e5 100644 --- a/tests/reload-behavior.test.ts +++ b/tests/reload-behavior.test.ts @@ -4,7 +4,7 @@ import { UserMessageComponent, type ExtensionAPI, } from "@earendil-works/pi-coding-agent"; -import { Text } from "@earendil-works/pi-tui"; + import toolDisplayExtension from "../src/index.ts"; import { renderBashCall } from "../src/bash-display.ts"; import registerNativeUserMessageBox from "../src/user-message-box-native.ts"; @@ -34,6 +34,15 @@ interface ToolLike { [key: string]: unknown; } +const sessionCtx = { ui: { theme: {}, notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }; + +/** Invoke all captured session_start handlers (simulates Pi calling the extension). */ +async function invokeSessionStart(capturedHandlers: CapturedHandler[]): Promise { + for (const { event, handler } of capturedHandlers) { + if (event === "session_start") await handler({}, sessionCtx); + } +} + /** * Create a minimal ExtensionAPI stub that captures registrations for later * inspection. Mirrors the pattern from index-integration.test.ts. @@ -115,7 +124,7 @@ test("1: after reload, new lifecycle handlers are registered", () => { }); // --------------------------------------------------------------------------- -// 3. Session cleanup and Bash spinner lifecycle +// 3. Session cleanup and Bash command rendering // --------------------------------------------------------------------------- test("3: session cleanup runs once per registration and accepts the next session", () => { @@ -130,178 +139,38 @@ test("3: session cleanup runs once per registration and accepts the next session assert.equal(cleanups, 2); }); - -test("3: bash spinner interval is created during partial execution and cleared on completion", () => { +test("3: bash renderBashCall does not create timers", () => { const originalSetInterval = globalThis.setInterval; - const originalClearInterval = globalThis.clearInterval; - - const createdIntervals: ReturnType[] = []; - const clearedIntervals: ReturnType[] = []; - - // Mock setInterval to track creations + let intervalCreated = false; globalThis.setInterval = ((fn: (...args: unknown[]) => unknown, ms?: number, ..._args: unknown[]) => { - const id = originalSetInterval(fn as (...args: unknown[]) => unknown, ms ?? 0); - createdIntervals.push(id); - return id; + intervalCreated = true; + return originalSetInterval(fn, ms ?? 0); }) as typeof globalThis.setInterval; - // Mock clearInterval to track clearings - globalThis.clearInterval = ((id: ReturnType) => { - clearedIntervals.push(id); - originalClearInterval(id); - }) as typeof globalThis.clearInterval; - - try { - // Render context that triggers spinner - const textComponent = new Text("", 0, 0); - const context: Record = { - executionStarted: true, - isPartial: true, - invalidate: () => {}, - lastComponent: textComponent, - state: {}, - }; - - // Create spinner - const result1 = renderBashCall( - { command: "sleep 5" }, - stubTheme, - context as unknown as Parameters[2], - ); - assert.equal(typeof result1.render, "function", "renderBashCall returns a component"); - assert.ok( - createdIntervals.length > 0, - "spinner interval was created during partial execution", - ); - - // Simulate execution completing (reload-like: context becomes non-partial) - context.isPartial = false; - context.lastComponent = result1; - const result2 = renderBashCall( - { command: "sleep 5" }, - stubTheme, - context as unknown as Parameters[2], - ); - assert.equal(result2, result1, "renderBashCall reuses its component"); - assert.ok( - clearedIntervals.length > 0, - "spinner interval was cleared on execution completion", - ); - - // Verify the created interval was also cleared - const allCreatedCleared = createdIntervals.every((id) => - clearedIntervals.includes(id), - ); - assert.ok(allCreatedCleared, "all created spinner intervals were cleared"); - } finally { - globalThis.setInterval = originalSetInterval; - globalThis.clearInterval = originalClearInterval; - // Clean up any remaining intervals - for (const id of createdIntervals) { - if (!clearedIntervals.includes(id)) { - originalClearInterval(id); - } - } - } -}); - -test("3: session cleanup clears an active Bash spinner and its row state", () => { - const originalClearInterval = globalThis.clearInterval; - const clearedIntervals: ReturnType[] = []; - globalThis.clearInterval = ((id: ReturnType) => { - clearedIntervals.push(id); - originalClearInterval(id); - }) as typeof globalThis.clearInterval; - const state: Record = {}; try { renderBashCall({ command: "sleep 5" }, stubTheme, { executionStarted: true, isPartial: true, - invalidate: () => {}, - state, }); - disposeSession(); - assert.ok(clearedIntervals.length > 0); - assert.deepEqual(state, {}); + assert.equal(intervalCreated, false, "no setInterval should be called"); } finally { - globalThis.clearInterval = originalClearInterval; - disposeSession(); + globalThis.setInterval = originalSetInterval; } }); -test("3: session cleanup clears Bash row state even without an animation timer", () => { - const state: Record = {}; - renderBashCall({ command: "sleep 5" }, stubTheme, { +test("3: bash renderBashCall returns consistent component for same lastComponent", () => { + const first = renderBashCall({ command: "sleep 5" }, stubTheme, { executionStarted: true, isPartial: true, - state, }); - disposeSession(); - - assert.deepEqual(state, {}); -}); - -test("3: session cleanup clears every row state sharing a tool call", () => { - const firstState: Record = {}; - const secondState: Record = {}; - const context = { executionStarted: true, isPartial: true, toolCallId: "shared" }; - renderBashCall({ command: "sleep 5" }, stubTheme, { ...context, state: firstState }); - renderBashCall({ command: "sleep 5" }, stubTheme, { ...context, state: secondState }); - - disposeSession(); - - assert.deepEqual(firstState, {}); - assert.deepEqual(secondState, {}); -}); - -test("3: multiple consecutive bash render calls do not create duplicate timers", () => { - const originalSetInterval = globalThis.setInterval; - const originalClearInterval = globalThis.clearInterval; - - const createdIntervals: ReturnType[] = []; - - globalThis.setInterval = ((fn: (...args: unknown[]) => unknown, ms?: number, ..._args: unknown[]) => { - const id = originalSetInterval(fn, ms ?? 0); - createdIntervals.push(id); - return id; - }) as typeof globalThis.setInterval; - - globalThis.clearInterval = ((id: ReturnType) => { - originalClearInterval(id); - }) as typeof globalThis.clearInterval; - - try { - const textComponent = new Text("", 0, 0); - const context: Record = { - executionStarted: true, - isPartial: true, - invalidate: () => {}, - lastComponent: textComponent, - state: {}, - }; - - // Call renderBashCall multiple times - should only create ONE timer - renderBashCall({ command: "test" }, stubTheme, context as unknown as Parameters[2]); - const afterFirst = createdIntervals.length; - - renderBashCall({ command: "test" }, stubTheme, context as unknown as Parameters[2]); - const afterSecond = createdIntervals.length; - - // The timer should only be created once (guarded by spinnerState.timer check) - assert.equal( - afterSecond, - afterFirst, - "duplicate renderBashCall does not create another timer", - ); + const second = renderBashCall({ command: "sleep 5" }, stubTheme, { + executionStarted: true, + isPartial: false, + lastComponent: first, + }); - // Complete execution - context.isPartial = false; - renderBashCall({ command: "test" }, stubTheme, context as unknown as Parameters[2]); - } finally { - globalThis.setInterval = originalSetInterval; - globalThis.clearInterval = originalClearInterval; - } + assert.equal(first, second, "should reuse component via lastComponent"); }); // --------------------------------------------------------------------------- @@ -312,16 +181,17 @@ test("3: multiple consecutive bash render calls do not create duplicate timers", // 5. User message box cleanup // --------------------------------------------------------------------------- -test("5: UserMessageComponent prototype is patched on first call and safe on reload", () => { - const { api } = createApiStub(); +test("5: UserMessageComponent prototype is patched on first call and safe on reload", async () => { + const { api, capturedHandlers } = createApiStub(); const proto = UserMessageComponent.prototype as PatchableUserMessagePrototype; // Before any patching const originalRenderBefore = proto.__piUserMessageOriginalRender; - // First call patches it + // First call + session_start triggers patching toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); assert.ok( proto.__piUserMessageNativePatched, "prototype is marked as patched after first call", @@ -334,7 +204,8 @@ test("5: UserMessageComponent prototype is patched on first call and safe on rel const firstOriginalRender = proto.__piUserMessageOriginalRender; // Reload (second call) should be safe - assert.doesNotThrow(() => toolDisplayExtension(api)); + toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); assert.ok( proto.__piUserMessageNativePatched, @@ -347,7 +218,7 @@ test("5: UserMessageComponent prototype is patched on first call and safe on rel ); }); -test("5: patchNativeUserMessagePrototype can be called multiple times safely", () => { +test("5: patchNativeUserMessagePrototype can be called multiple times safely", async () => { const proto = UserMessageComponent.prototype as PatchableUserMessagePrototype; // Track the original render before any patching (tests share process state, @@ -356,7 +227,9 @@ test("5: patchNativeUserMessagePrototype can be called multiple times safely", ( const wasAlreadyPatched = !!proto.__piUserMessageNativePatched; // Patch via full extension (uses a fresh api stub each time) - assert.doesNotThrow(() => toolDisplayExtension(createApiStub().api)); + const { api, capturedHandlers } = createApiStub(); + toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); // After calling, the prototype should be patched assert.ok( @@ -387,9 +260,9 @@ test("5: patchNativeUserMessagePrototype can be called multiple times safely", ( ); // Re-patching via another extension call is safe (no throw) - assert.doesNotThrow(() => { - toolDisplayExtension(createApiStub().api); - }); + const { api: api2, capturedHandlers: handlers2 } = createApiStub(); + toolDisplayExtension(api2); + await invokeSessionStart(handlers2); // The patched flag and original render ref remain stable assert.ok( @@ -406,10 +279,11 @@ test("5: patchNativeUserMessagePrototype can be called multiple times safely", ( // 6. Command unregistration / re-registration // --------------------------------------------------------------------------- -test("6: /tool-display command is registered on first call and re-registered on reload", () => { - const { api, capturedCommands } = createApiStub(); +test("6: /tool-display command is registered on first call and re-registered on reload", async () => { + const { api, capturedCommands, capturedHandlers } = createApiStub(); toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); const firstToolDisplayCmds = capturedCommands.filter( (c) => c.name === "tool-display", ); @@ -417,6 +291,7 @@ test("6: /tool-display command is registered on first call and re-registered on // Reload toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); const secondToolDisplayCmds = capturedCommands.filter( (c) => c.name === "tool-display", ); @@ -472,7 +347,7 @@ test("8: session_start handler can be invoked after reload without errors", asyn // Invoke it once await assert.doesNotReject(async () => - sessionHandler!({}, { ui: { theme: {}, notify: () => {} } }), + sessionHandler!({}, { ui: { theme: {}, notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }), ); // Reload @@ -485,7 +360,7 @@ test("8: session_start handler can be invoked after reload without errors", asyn )?.handler; assert.ok(firstSessionHandler, "session_start handler exists after reload"); await assert.doesNotReject(async () => - firstSessionHandler!({}, { ui: { theme: {}, notify: () => {} } }), + firstSessionHandler!({}, { ui: { theme: {}, notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }), ); }); @@ -493,19 +368,22 @@ test("8: session_start handler can be invoked after reload without errors", asyn // 9. Double reload safety // --------------------------------------------------------------------------- -test("9: calling toolDisplayExtension three times (double reload) is safe", () => { - const { api, capturedTools, capturedCommands } = createApiStub(); +test("9: calling toolDisplayExtension three times (double reload) is safe", async () => { + const { api, capturedTools, capturedCommands, capturedHandlers } = createApiStub(); - // First call + // First call + session_start toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); const afterFirst = { tools: capturedTools.length, cmds: capturedCommands.length }; - // First reload + // First reload + session_start toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); const afterSecond = { tools: capturedTools.length, cmds: capturedCommands.length }; - // Second reload (double reload) - assert.doesNotThrow(() => toolDisplayExtension(api)); + // Second reload (double reload) + session_start + toolDisplayExtension(api); + await invokeSessionStart(capturedHandlers); const afterThird = { tools: capturedTools.length, cmds: capturedCommands.length }; // Tool registration is deferred until owners and active tools are known. @@ -536,134 +414,62 @@ test("9: calling toolDisplayExtension three times (double reload) is safe", () = } }); -test("9: no duplicate setInterval across rapid reload-like scenarios", () => { +test("9: no timers created across rapid reload-like scenarios", () => { const originalSetInterval = globalThis.setInterval; - const originalClearInterval = globalThis.clearInterval; - - const createdIntervals: ReturnType[] = []; + let intervalCount = 0; globalThis.setInterval = ((fn: (...args: unknown[]) => unknown, ms?: number, ..._args: unknown[]) => { - const id = originalSetInterval(fn, ms ?? 0); - createdIntervals.push(id); - return id; + intervalCount++; + return originalSetInterval(fn, ms ?? 0); }) as typeof globalThis.setInterval; - globalThis.clearInterval = ((id: ReturnType) => { - originalClearInterval(id); - }) as typeof globalThis.clearInterval; - try { // Create two independent contexts (simulating two rapid calls) const ctx1: Record = { executionStarted: true, isPartial: true, - invalidate: () => {}, - lastComponent: new Text("", 0, 0), - state: {}, }; const ctx2: Record = { executionStarted: true, isPartial: true, - invalidate: () => {}, - lastComponent: new Text("", 0, 0), - state: {}, }; // Simulate two rapid render calls (like double reload) renderBashCall({ command: "test" }, stubTheme, ctx1 as unknown as Parameters[2]); renderBashCall({ command: "test" }, stubTheme, ctx2 as unknown as Parameters[2]); - // Each context should get its own timer - assert.equal(createdIntervals.length, 2, "two contexts = two timers"); - - // Clean up both - ctx1.isPartial = false; - ctx2.isPartial = false; - renderBashCall({ command: "test" }, stubTheme, ctx1 as unknown as Parameters[2]); - renderBashCall({ command: "test" }, stubTheme, ctx2 as unknown as Parameters[2]); + // No timers should be created since spinner is removed + assert.equal(intervalCount, 0, "no timers created"); } finally { globalThis.setInterval = originalSetInterval; - globalThis.clearInterval = originalClearInterval; - for (const id of createdIntervals) { - originalClearInterval(id); - } } }); // --------------------------------------------------------------------------- -// 10. Partial reload (active bash spinner mid-animation) +// 10. Partial reload (bash command rendering without spinner) // --------------------------------------------------------------------------- -test("10: active bash spinner timer is cleaned up when execution transitions from partial to complete", () => { - const originalSetInterval = globalThis.setInterval; - const originalClearInterval = globalThis.clearInterval; - - const createdIntervals: ReturnType[] = []; - const clearedIntervals: ReturnType[] = []; - - globalThis.setInterval = ((fn: (...args: unknown[]) => unknown, ms?: number, ..._args: unknown[]) => { - const id = originalSetInterval(fn, ms ?? 0); - createdIntervals.push(id); - return id; - }) as typeof globalThis.setInterval; - - globalThis.clearInterval = ((id: ReturnType) => { - clearedIntervals.push(id); - originalClearInterval(id); - }) as typeof globalThis.clearInterval; - - try { - const textComponent = new Text("", 0, 0); - const context: Record = { - executionStarted: true, - isPartial: true, - invalidate: () => {}, - lastComponent: textComponent, - state: {}, - }; - - // Start spinner (mid-animation) - renderBashCall({ command: "long-running-task" }, stubTheme, context as unknown as Parameters[2]); - assert.equal(createdIntervals.length, 1, "one timer created for spinner"); - - // Simulate partial reload: execution not started yet in new context - // (e.g., reload happens while bash is still running) - const newContext: Record = { - executionStarted: false, - isPartial: false, - invalidate: () => {}, - lastComponent: new Text("", 0, 0), - state: {}, - }; - - // New render call with fresh context - no timer should be created since - // execution hasn't started - renderBashCall({ command: "long-running-task" }, stubTheme, newContext as unknown as Parameters[2]); - assert.equal( - createdIntervals.length, - 1, - "no new timer for non-executing context", - ); - - // Complete the original execution - context.isPartial = false; - renderBashCall({ command: "long-running-task" }, stubTheme, context as unknown as Parameters[2]); +test("10: bash command renders consistently across partial and complete states", () => { + const context: Record = { + executionStarted: true, + isPartial: true, + }; - // Original timer should have been cleared - assert.ok( - clearedIntervals.length > 0, - "original spinner timer cleared on completion", - ); - } finally { - globalThis.setInterval = originalSetInterval; - globalThis.clearInterval = originalClearInterval; - for (const id of createdIntervals) { - if (!clearedIntervals.includes(id)) { - originalClearInterval(id); - } - } - } + // Render during partial execution + const partial = renderBashCall({ command: "long-running-task" }, stubTheme, context as unknown as Parameters[2]); + const partialText = partial.render(120).join("\n"); + assert.match(partialText, /long-running-task/); + assert.doesNotMatch(partialText, /^⠋/, "no spinner frame"); + + // Complete the execution reusing the component + context.isPartial = false; + context.lastComponent = partial; + const complete = renderBashCall({ command: "long-running-task" }, stubTheme, context as unknown as Parameters[2]); + assert.equal(partial, complete, "component is reused via lastComponent"); + const completeText = complete.render(120).join("\n"); + assert.match(completeText, /long-running-task/); + assert.equal(partialText, completeText, "same output regardless of partial state"); }); // --------------------------------------------------------------------------- @@ -786,19 +592,21 @@ test("14: open settings modal onClose callback can be invoked multiple times", ( assert.ok(true, "onClose can be called multiple times safely"); }); -test("14: extension re-initialization does not leave stale modal references", () => { +test("14: extension re-initialization does not leave stale modal references", async () => { // When the extension is re-loaded, the old controller and modal closures // are replaced. The new extension function creates fresh closures. // This test verifies the old references don't interfere. - const { api: api1, capturedCommands: cmds1 } = createApiStub(); + const { api: api1, capturedCommands: cmds1, capturedHandlers: handlers1 } = createApiStub(); toolDisplayExtension(api1); + await invokeSessionStart(handlers1); const firstCommandHandler = cmds1.find((c) => c.name === "tool-display")?.handler; // Reload - const { api: api2, capturedCommands: cmds2 } = createApiStub(); + const { api: api2, capturedCommands: cmds2, capturedHandlers: handlers2 } = createApiStub(); toolDisplayExtension(api2); + await invokeSessionStart(handlers2); const secondCommandHandler = cmds2.find((c) => c.name === "tool-display")?.handler; @@ -832,7 +640,7 @@ test("lifecycle: full session lifecycle (init→reload→invoke handlers) does n if (event === "message_update" || event === "message_end" || event === "context") { continue; // These need specific event shapes; tested separately } - const result = handler({}, { ui: { theme: {}, notify: () => {} } }); + const result = handler({}, { ui: { theme: {}, notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }); if (result instanceof Promise) { await assert.doesNotReject( () => result, @@ -849,7 +657,7 @@ test("lifecycle: full session lifecycle (init→reload→invoke handlers) does n if (event === "message_update" || event === "message_end" || event === "context") { continue; } - const result = handler({}, { ui: { theme: {}, notify: () => {} } }); + const result = handler({}, { ui: { theme: {}, notify: () => {} }, cwd: process.cwd(), isProjectTrusted: () => false }); if (result instanceof Promise) { await assert.doesNotReject( () => result, diff --git a/tests/render-utils.test.ts b/tests/render-utils.test.ts index 129ffd7..9cf1d18 100644 --- a/tests/render-utils.test.ts +++ b/tests/render-utils.test.ts @@ -618,7 +618,7 @@ test("buildCollapsedDiffHintText: wide width shows full hint with lines and hunk ); assert.equal( hint, - "… (42 more diff lines • 3 more hunks • Ctrl+O to expand)", + "… (42 more visual diff lines • 3 more hunks • Ctrl+O to expand)", ); }); @@ -628,7 +628,7 @@ test("buildCollapsedDiffHintText: remaining lines only, no hunks", () => { 80, codePointWidthOps, ); - assert.equal(hint, "… (10 more diff lines • Ctrl+O to expand)"); + assert.equal(hint, "… (10 more visual diff lines • Ctrl+O to expand)"); }); test("buildCollapsedDiffHintText: zero remaining lines and zero hunks", () => { @@ -637,8 +637,7 @@ test("buildCollapsedDiffHintText: zero remaining lines and zero hunks", () => { 80, codePointWidthOps, ); - // "0 more diff lines" passed to pluralize → "0 more diff lines" - assert.equal(hint, "… (0 more diff lines • Ctrl+O to expand)"); + assert.equal(hint, "… (0 more visual diff lines • Ctrl+O to expand)"); }); test("buildCollapsedDiffHintText: narrow width forces shorter variants", () => { diff --git a/tests/renderer-adapter-registration.test.ts b/tests/renderer-adapter-registration.test.ts index 9307a74..163846c 100644 --- a/tests/renderer-adapter-registration.test.ts +++ b/tests/renderer-adapter-registration.test.ts @@ -5,6 +5,7 @@ import { disposeAll, resetDisposed } from "../src/disposable.ts"; import { registerToolDisplayApi } from "../src/tool-overrides.ts"; import { createToolDisplayResolver } from "../src/tool-display-resolver.ts"; import { DEFAULT_TOOL_DISPLAY_CONFIG } from "../src/types.ts"; +import { registerRendererAdapter } from "../tool-display-api-consumer.js"; const row = { toolName: "third_party", arguments: {}, builtIn: false } as const; @@ -18,6 +19,19 @@ test("producer registration is disposable, deterministic, and leaves its input u assert.deepEqual(adapter, { id: "producer-a", toolName: "third_party", kind: "generic" }); }); +test("an explicit disabled custom override keeps native presentation ahead of producer intent", () => { + const dispose = registerProducerRendererAdapter({ id: "producer-disabled", toolName: "third_party", kind: "generic" }); + try { + const config = { + ...DEFAULT_TOOL_DISPLAY_CONFIG, + customToolOverrides: { + third_party: { enabled: false, kind: "generic" as const, outputMode: "preview" as const, overrideCallRenderer: true }, + }, + }; + assert.equal(createRendererCatalog().resolve(row, config, {}), undefined); + } finally { dispose(); } +}); + test("producer callbacks receive detached arguments, results, and context args", () => { const args = { nested: { path: "original.txt" } }; const result = { content: [{ type: "text", text: "original" }], details: { count: 1 } }; @@ -77,6 +91,53 @@ test("equal-priority producer conflicts fail deterministically regardless of ord } finally { second(); first(); } }); +test("early producer registration preserves modern output and call-override fields", () => { + resetDisposed(); + const apiSymbol = Symbol.for("pi-tool-display.api.v1"); + delete (globalThis as any)[apiSymbol]; + const disposePending = registerRendererAdapter({ + id: "early-modern", + toolName: "early_modern", + kind: "generic", + outputMode: "hidden", + overrideCallRenderer: true, + }); + try { + registerToolDisplayApi(() => DEFAULT_TOOL_DISPLAY_CONFIG); + const nativeCall = () => ({ render: () => ["native call"] }); + const plan = createRendererCatalog().resolve( + { toolName: "early_modern", arguments: {}, builtIn: false }, + DEFAULT_TOOL_DISPLAY_CONFIG, + { call: nativeCall }, + )!; + assert.notStrictEqual(plan.call, nativeCall); + assert.deepEqual(plan.result!({ content: [{ type: "text", text: "secret" }] }, { expanded: false }, { fg: (_c: string, text: string) => text, bold: (text: string) => text }).render(80), []); + } finally { + disposePending(); + disposeAll(); + } +}); + +test("reused global API keeps producer intent and remains disposable by the current epoch", () => { + resetDisposed(); + registerToolDisplayApi(() => DEFAULT_TOOL_DISPLAY_CONFIG); + const apiSymbol = Symbol.for("pi-tool-display.api.v1"); + const api = (globalThis as any)[apiSymbol]; + api.registerAdapter({ id: "epoch-producer", toolName: "epoch_tool", kind: "generic" }); + registerToolDisplayApi(() => ({ ...DEFAULT_TOOL_DISPLAY_CONFIG, previewLines: 1 })); + assert.strictEqual((globalThis as any)[apiSymbol], api); + assert.ok(createRendererCatalog().resolve({ toolName: "epoch_tool", arguments: {} }, DEFAULT_TOOL_DISPLAY_CONFIG, {})); + + disposeAll(); + assert.equal((globalThis as any)[apiSymbol], undefined); + assert.throws( + () => api.registerAdapter({ id: "retired", toolName: "retired_tool", kind: "generic" }), + /disposed/, + ); + assert.equal(createRendererCatalog().resolve({ toolName: "epoch_tool", arguments: {} }, DEFAULT_TOOL_DISPLAY_CONFIG, {}), undefined); + assert.equal(createRendererCatalog().resolve({ toolName: "retired_tool", arguments: {} }, DEFAULT_TOOL_DISPLAY_CONFIG, {}), undefined); +}); + test("legacy read and edit adapter kinds retain their specialized display without mutating definitions", () => { resetDisposed(); const read = Object.freeze({ name: "legacy_read", execute: Object.freeze(() => undefined) }); diff --git a/tests/runtime-matrix.json b/tests/runtime-matrix.json index 45205d0..bd7a150 100644 --- a/tests/runtime-matrix.json +++ b/tests/runtime-matrix.json @@ -1,6 +1,5 @@ [ { "name": "development", "env": "PI_RUNTIME_DEV_ROOT", "required": true }, { "name": "pi-0.81.1", "version": "0.81.1", "env": "PI_RUNTIME_0_81_1_ROOT", "required": true }, - { "name": "pi-0.82.0", "version": "0.82.0", "env": "PI_RUNTIME_0_82_0_ROOT", "required": true }, - { "name": "minimum-supported", "version": "0.74.0", "env": "PI_RUNTIME_MIN_ROOT", "required": true } + { "name": "pi-0.82.0", "version": "0.82.0", "env": "PI_RUNTIME_0_82_0_ROOT", "required": true } ] diff --git a/tests/support/release-capability-lifecycle.ts b/tests/support/release-capability-lifecycle.ts index d1235bf..921109e 100644 --- a/tests/support/release-capability-lifecycle.ts +++ b/tests/support/release-capability-lifecycle.ts @@ -37,11 +37,15 @@ try { return row.render(120).join("\n"); }; - await handlers.get("session_start")?.at(-1)?.({}, { ui: { notify() {} } }); + const invokeAll = async (event: string) => { + for (const handler of handlers.get(event) ?? []) await handler({}, { ui: { notify() {} }, cwd: process.cwd(), isProjectTrusted: () => false }); + }; + + await invokeAll("session_start"); assert.doesNotMatch(frame("without-rtk"), /compacted by RTK/); assert.equal(snapshots, 1); commands = [{ name: "rtk" }]; - await handlers.get("before_agent_start")?.at(-1)?.(); + await invokeAll("before_agent_start"); assert.match(frame("with-rtk"), /compacted by RTK/); assert.equal(snapshots, 2); assert.match(frame("same-epoch"), /compacted by RTK/); diff --git a/tests/tool-execution-patch.test.ts b/tests/tool-execution-patch.test.ts index ad92ec2..460df2b 100644 --- a/tests/tool-execution-patch.test.ts +++ b/tests/tool-execution-patch.test.ts @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import test, { afterEach } from "node:test"; import { createBashTool, createEditTool, @@ -12,12 +12,16 @@ import { ToolExecutionComponent, type ExtensionAPI, } from "@earendil-works/pi-coding-agent"; -import { disposeAll, resetDisposed } from "../src/disposable.ts"; +import { disposeAll, disposeSession, resetDisposed } from "../src/disposable.ts"; import { registerToolExecutionPatch } from "../src/tool-execution-patch.ts"; import { registerToolDisplayApi } from "../src/tool-overrides.ts"; import { DEFAULT_TOOL_DISPLAY_CONFIG, type ToolDisplayConfig } from "../src/types.ts"; initTheme(undefined, false); + +afterEach(() => { + disposeSession(); +}); const theme = { fg: (_color: string, text: string) => text, bold: (text: string) => text }; const render = (component: unknown) => (component as { render(width: number): string[] }).render(120).join("\n").trim(); const plainRender = (component: unknown) => render(component).replace(/\x1b\[[0-9;]*m/g, ""); @@ -94,10 +98,13 @@ test("collapsed Bash output honors the visual-line budget across partial, histor isError: false, }; const renderRow = () => row.render(80).join("\n").replace(/\x1b\[[0-9;]*m/g, ""); + const bashCollapsedLines = 10; const assertFolded = (rendered: string) => { const visibleRows = rendered.split("\n").filter((line) => line.trim()); - assert.equal(visibleRows.findIndex((line) => /more visual lines/.test(line)), 11); - assert.match(rendered, /more visual lines .* Ctrl\+O to expand/); + const hintIndex = visibleRows.findIndex((line) => /more visual/.test(line)); + assert.ok(hintIndex >= 0, `visual omission hint must appear, got rows: ${visibleRows.length}`); + assert.ok(hintIndex <= bashCollapsedLines + 1, `hint should appear within budget, got index ${hintIndex}`); + assert.match(rendered, /more visual/); assert.match(rendered, /output truncated .* full output: \/tmp\/full-output/); assert.doesNotMatch(rendered, /fixture-9/); }; @@ -172,7 +179,7 @@ test("collapsed split diffs count logical content rows, not headers, metadata, o assert.match(collapsed, /HUNK_HEADER_PATTERN/); assert.match(collapsed, /SPLIT_SEPARATOR/); assert.doesNotMatch(collapsed, /SPLIT_HEADER_ROW_COUNT/); - assert.match(collapsed, /more diff lines .* Ctrl\+O to expand/); + assert.match(collapsed, /more visual diff lines .* Ctrl\+O to expand/); } finally { handlers.session_shutdown?.({ reason: "reload" }); } @@ -397,38 +404,36 @@ test("historical rows refresh display without changing ownership or execution", }); }); -test("session switches keep the installed host renderer patch", () => { +test("owned disposer restores the host renderer for every session transition", () => { const prototype = ToolExecutionComponent.prototype as any; const originalCall = prototype.getCallRenderer; const runtime = apiStub(); - registerToolExecutionPatch(runtime.api, () => config({ configured: true })); - const patchedCall = prototype.getCallRenderer; - assert.notStrictEqual(patchedCall, originalCall); + const dispose = registerToolExecutionPatch(runtime.api, () => config({ configured: true })); + assert.notStrictEqual(prototype.getCallRenderer, originalCall); - for (const reason of ["new", "resume", "fork"]) { - runtime.handlers.session_shutdown?.({ reason }); - assert.strictEqual(prototype.getCallRenderer, patchedCall); - } - runtime.handlers.session_shutdown?.({ reason: "quit" }); + dispose(); + assert.strictEqual(prototype.getCallRenderer, originalCall); + dispose(); assert.strictEqual(prototype.getCallRenderer, originalCall); }); -test("reload restores the prototype and reinstallation does not stack wrappers", () => { +test("disposal restores the prototype and reinstallation does not stack wrappers", () => { const prototype = ToolExecutionComponent.prototype as any; const originalCall = prototype.getCallRenderer; const originalResult = prototype.getResultRenderer; const first = apiStub(); - registerToolExecutionPatch(first.api, () => config({ configured: true })); + const disposeFirst = registerToolExecutionPatch(first.api, () => config({ configured: true })); const firstPatchedCall = prototype.getCallRenderer; - registerToolExecutionPatch(first.api, () => config({ configured: true })); + const disposeDuplicate = registerToolExecutionPatch(first.api, () => config({ configured: true })); assert.equal(prototype.getCallRenderer, firstPatchedCall); - first.handlers.session_shutdown?.({ reason: "reload" }); + disposeDuplicate(); + disposeFirst(); assert.equal(prototype.getCallRenderer, originalCall); assert.equal(prototype.getResultRenderer, originalResult); const second = apiStub(); - registerToolExecutionPatch(second.api, () => config({ configured: true })); + const disposeSecond = registerToolExecutionPatch(second.api, () => config({ configured: true })); assert.notEqual(prototype.getCallRenderer, originalCall); - second.handlers.session_shutdown?.({ reason: "reload" }); + disposeSecond(); assert.equal(prototype.getCallRenderer, originalCall); }); diff --git a/tests/tool-ui-utils.test.ts b/tests/tool-ui-utils.test.ts index fdab959..842147a 100644 --- a/tests/tool-ui-utils.test.ts +++ b/tests/tool-ui-utils.test.ts @@ -373,7 +373,7 @@ test("collapsed diff hint keeps full guidance when width allows", () => { codePointWidthOps, ); - assert.equal(hint, "… (12 more diff lines • 2 more hunks • Ctrl+O to expand)"); + assert.equal(hint, "… (12 more visual diff lines • 2 more hunks • Ctrl+O to expand)"); }); test("line width clamp never returns text wider than the requested width", () => { diff --git a/tests/user-message-box.test.ts b/tests/user-message-box.test.ts index 36d47b3..fce6fb2 100644 --- a/tests/user-message-box.test.ts +++ b/tests/user-message-box.test.ts @@ -581,6 +581,20 @@ test("unregister leaves a later foreign render patch intact", () => { assert.equal(prototype.__piUserMessagePatchOwner, undefined); }); +test("user-message patch disposers restore only their owned installation", () => { + const originalRender = (width: number) => [`orig:${width}`]; + const prototype: PatchableUserMessagePrototype = { render: originalRender }; + const first = patchUserMessageRenderPrototype(prototype, 7, base => width => [`first:${base(width)}`]); + const second = patchUserMessageRenderPrototype(prototype, 7, base => width => [`second:${base(width)}`]); + const secondRender = prototype.render; + + first(); + assert.strictEqual(prototype.render, secondRender); + assert.deepEqual(prototype.render(5), ["second:orig:5"]); + second(); + assert.strictEqual(prototype.render, originalRender); +}); + test("unregister restores the original render descriptor", () => { const originalRender = (width: number) => [`orig:${width}`]; const prototype = {} as PatchableUserMessagePrototype; diff --git a/tests/visual-preview-contract.test.ts b/tests/visual-preview-contract.test.ts new file mode 100644 index 0000000..67d84bb --- /dev/null +++ b/tests/visual-preview-contract.test.ts @@ -0,0 +1,215 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { visibleWidth } from "@earendil-works/pi-tui"; +import { createRendererCatalog } from "../src/renderer-catalog.ts"; +import { createToolDisplayResolver } from "../src/tool-display-resolver.ts"; +import { DEFAULT_TOOL_DISPLAY_CONFIG, type ToolDisplayConfig } from "../src/types.ts"; + +const theme = { fg: (_color: string, text: string) => text, bold: (text: string) => text }; +const result = (text: string, details: object = {}) => ({ content: [{ type: "text", text }], details }); +const options = (expanded = false, isPartial = false) => ({ expanded, isPartial }); + +function resolveResult(toolName: string, config: Partial, arguments_: Record = {}, builtIn = true) { + return createToolDisplayResolver( + () => ({ + ...DEFAULT_TOOL_DISPLAY_CONFIG, + ...config, + builtInToolDisplays: { ...DEFAULT_TOOL_DISPLAY_CONFIG.builtInToolDisplays, ...config.builtInToolDisplays }, + }), + createRendererCatalog(), + ).resolve({ toolName, arguments: arguments_, builtIn }, {}).result!; +} + +function assertWidthSafe(lines: string[], width: number): void { + for (const line of lines) assert.ok(visibleWidth(line) <= width, `${visibleWidth(line)} > ${width}: ${JSON.stringify(line)}`); +} + +test("Bash success preview budgets complete text by visual lines", () => { + const renderResult = resolveResult("bash", { bashOutputMode: "preview", previewLines: 1 }, { command: "printf" }); + const lines = renderResult(result("123456789012345\nb\nc"), options(), theme, { args: { command: "printf" } }).render(10); + + assert.deepEqual(lines, ["1234567890", "… (+3)"]); + assertWidthSafe(lines, 10); +}); + +test("zero Bash body budget still reports omitted visual rows for partial and complete output", () => { + const renderResult = resolveResult("bash", { bashOutputMode: "opencode", bashCollapsedLines: 0 }, { command: "printf" }); + for (const isPartial of [false, true]) { + const lines = renderResult(result("one\ntwo"), options(false, isPartial), theme, { args: { command: "printf" } }).render(40); + assert.deepEqual(lines, ["… (2 more visual lines)"]); + } +}); + +test("expanded Bash error preview caps complete text by visual lines", () => { + const renderResult = resolveResult("bash", { bashErrorOutputMode: "preview", expandedPreviewMaxLines: 1 }, { command: "false" }); + const lines = renderResult(result("123456789012345\nb\nc"), options(true), theme, { args: { command: "false" }, isError: true }).render(10); + + assert.deepEqual(lines.filter((line: string) => line.startsWith("…")), ["… (+3)"]); + assert.doesNotMatch(lines.join("\n"), /\nb\n|\nc\n|display capped at/); + assertWidthSafe(lines, 10); +}); + +test("Bash partial preview preserves complete body text before visual folding", () => { + const renderResult = resolveResult("bash", { bashOutputMode: "preview", previewLines: 2 }, { command: "printf" }); + const lines = renderResult(result("a\n\n\nb"), options(false, true), theme, { args: { command: "printf" } }).render(20); + + assert.deepEqual(lines.map((line: string) => line.trimEnd()), ["a", "", "… (+2)"]); + assertWidthSafe(lines, 20); +}); + +test("Read preview passes complete text to collapsed and expanded visual budgets", () => { + const renderResult = resolveResult("read", { readOutputMode: "preview", previewLines: 1, expandedPreviewMaxLines: 1 }, { path: "fixture.txt" }); + for (const expanded of [false, true]) { + const lines = renderResult(result("123456789012345\nb\nc"), options(expanded), theme).render(10); + assert.deepEqual(lines, ["1234567890", "… (+3)"]); + assertWidthSafe(lines, 10); + } +}); + +for (const toolName of ["grep", "find", "ls"] as const) { + test(`${toolName} search preview uses the shared complete-text visual budget`, () => { + const renderResult = resolveResult(toolName, { searchOutputMode: "preview", previewLines: 1 }, { pattern: "x" }); + const lines = renderResult(result("123456789012345\nb\nc"), options(), theme).render(10); + + assert.deepEqual(lines, ["1234567890", "… (+3)"]); + assertWidthSafe(lines, 10); + }); +} + +test("MCP preview keeps display omission singular and backend truncation separate", () => { + const override = { enabled: true, kind: "mcp" as const, outputMode: "preview" as const, overrideCallRenderer: false }; + const renderResult = resolveResult("mcp", { previewLines: 1, showTruncationHints: true, customToolOverrides: { mcp: override } }, {}, false); + const lines = renderResult(result("one\ntwo", { truncation: { truncated: true } }), options(), theme).render(80); + const output = lines.join("\n"); + + assert.match(output, /1 more visual line • Ctrl\+O to expand/); + assert.equal((output.match(/more visual/g) ?? []).length, 1); + assert.match(output, /truncated by backend limits/); + assertWidthSafe(lines, 80); +}); + +test("Custom preview uses visual rows and retains an omission marker at narrow widths", () => { + const override = { enabled: true, kind: "generic" as const, outputMode: "preview" as const, overrideCallRenderer: false }; + const renderResult = resolveResult("custom", { previewLines: 1, customToolOverrides: { custom: override } }, {}, false); + + for (const width of [1, 2, 5]) { + const lines = renderResult(result("abcdef\nx"), options(), theme).render(width); + assert.equal(lines.length, 2); + assert.match(lines[1] ?? "", /…/); + assertWidthSafe(lines, width); + } +}); + +test("shared visual preview handles required widths and text widths", () => { + const renderResult = resolveResult("read", { readOutputMode: "preview", previewLines: 1 }, { path: "fixture.txt" }); + for (const width of [20, 40, 120, 10_000]) { + const lines = renderResult(result("x".repeat(width + 1)), options(), theme).render(width); + assert.equal(lines.length, 2); + assert.match(lines[1] ?? "", width < 40 ? /\+1/ : /1 more visual line/); + assertWidthSafe(lines, width); + } + + for (const text of ["\x1b[31m12345678901\x1b[0m", "界界界界界界", "🙂🙂🙂🙂🙂🙂"]) { + const lines = renderResult(result(text), options(), theme).render(10); + assert.equal(lines.length, 2); + assert.match(lines[1] ?? "", /1 more visual line|…/); + assertWidthSafe(lines, 10); + } +}); + +test("default and unlimited expanded visual budgets keep their public semantics", () => { + const defaultPreview = resolveResult("read", { readOutputMode: "preview" }, { path: "fixture.txt" }); + const collapsed = defaultPreview(result(Array.from({ length: 9 }, (_, index) => `row-${index}`).join("\n")), options(), theme).render(120); + assert.match(collapsed.at(-1) ?? "", /1 more visual line/); + + const unlimited = resolveResult("read", { readOutputMode: "preview", expandedPreviewMaxLines: 0 }, { path: "fixture.txt" }); + const expanded = unlimited(result("one\ntwo\nthree"), options(true), theme).render(40); + assert.deepEqual(expanded.map((line) => line.trimEnd()), ["one", "two", "three"]); +}); + +test("Diff collapsed and expanded budgets select logical lines but report omitted visual Diff lines", () => { + const renderResult = resolveResult("edit", { + diffViewMode: "unified", + diffCollapsedLines: 1, + expandedPreviewMaxLines: 1, + diffWordWrap: true, + }, { path: "fixture.txt" }); + const diff = `-${"OLD-" + "o".repeat(146)}\n+${"NEW-" + "n".repeat(146)}\n tail`; + + for (const expanded of [false, true]) { + const lines = renderResult(result("done", { diff }), options(expanded), theme).render(80); + const output = lines.join("\n").replace(/\x1b\[[0-9;]*m/g, ""); + assert.match(output, /OLD-/); + assert.doesNotMatch(output, /NEW-|tail/); + assert.match(output, /4 more visual diff lines/); + assert.equal((output.match(/more visual diff/g) ?? []).length, 1); + assert.equal(output.includes("Ctrl+O to expand"), !expanded); + assertWidthSafe(lines, 80); + } +}); + +test("Diff visual omission count responds to width while logical selection stays fixed", () => { + const renderResult = resolveResult("edit", { diffViewMode: "unified", diffCollapsedLines: 1, diffWordWrap: true }, { path: "fixture.txt" }); + const diff = `-${"OLD-" + "o".repeat(146)}\n+${"NEW-" + "n".repeat(146)}\n tail`; + const counts = new Map(); + for (const width of [40, 120]) { + const output = renderResult(result("done", { diff }), options(), theme).render(width).join("\n").replace(/\x1b\[[0-9;]*m/g, ""); + assert.match(output, /OLD-/); + assert.doesNotMatch(output, /NEW-|tail/); + const count = Number(/(\d+) more visual diff lines?/.exec(output)?.[1]); + assert.ok(Number.isInteger(count) && count > 0); + counts.set(width, count); + } + assert.ok((counts.get(40) ?? 0) > (counts.get(120) ?? 0)); +}); + +test("Diff logical budget hides metadata belonging only to omitted hunks", () => { + const renderResult = resolveResult("edit", { diffViewMode: "unified", diffCollapsedLines: 1, diffWordWrap: true }, { path: "fixture.txt" }); + const diff = [ + "@@ -1,1 +1,1 @@ first-hunk", + "-first old", + "+first new", + "@@ -20,1 +20,1 @@ second-hunk", + "-second old", + "+second new", + ].join("\n"); + const output = renderResult(result("done", { diff }), options(), theme).render(80).join("\n").replace(/\x1b\[[0-9;]*m/g, ""); + + assert.match(output, /first-hunk/); + assert.match(output, /first old/); + assert.doesNotMatch(output, /second-hunk|second old|second new/); + assert.match(output, /more visual diff lines/); +}); + +test("Diff logical budget hides file metadata belonging to omitted files", () => { + const renderResult = resolveResult("edit", { diffViewMode: "unified", diffCollapsedLines: 1, diffWordWrap: true }, { path: "fixture.txt" }); + const diff = [ + "diff --git a/first.txt b/first.txt", + "--- a/first.txt", + "+++ b/first.txt", + "@@ -1 +1 @@", + "-first old", + "+first new", + "diff --git a/second.txt b/second.txt", + "--- a/second.txt", + "+++ b/second.txt", + "@@ -1 +1 @@", + "-second old", + "+second new", + ].join("\n"); + const output = renderResult(result("done", { diff }), options(), theme).render(80).join("\n").replace(/\x1b\[[0-9;]*m/g, ""); + + assert.match(output, /first\.txt|first old/); + assert.doesNotMatch(output, /second\.txt|second old|second new/); + assert.match(output, /more visual diff lines/); +}); + +test("Diff omission hint uses singular visual-line wording", () => { + const renderResult = resolveResult("edit", { diffViewMode: "unified", diffCollapsedLines: 1, diffWordWrap: true }, { path: "fixture.txt" }); + const lines = renderResult(result("done", { diff: "-old\n+new" }), options(), theme).render(80); + const output = lines.join("\n").replace(/\x1b\[[0-9;]*m/g, ""); + + assert.match(output, /1 more visual diff line • Ctrl\+O to expand/); + assert.doesNotMatch(output, /1 more visual diff lines/); + assertWidthSafe(lines, 80); +});