dsh-openwrite 0.2.28 → 0.2.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +49 -8
  3. package/THIRD_PARTY_NOTICES.md +4 -3
  4. package/companions.mjs +508 -0
  5. package/cordis.patch.yml +0 -2
  6. package/docs/CLOSED_ISSUE_FOLLOWUPS.md +1 -1
  7. package/docs/COMPANION_PLUGINS.md +142 -0
  8. package/docs/CONTEXT_SLOTS.md +1 -1
  9. package/docs/COST_MEASUREMENT.md +2 -2
  10. package/docs/DEVELOPMENT.md +6 -11
  11. package/docs/DSH_STORE.md +14 -2
  12. package/docs/EDITOR_SAFETY_REPAIRS.md +2 -2
  13. package/docs/INSTALL.md +112 -4
  14. package/docs/ISSUES_121_123.md +4 -4
  15. package/docs/ISSUES_140_142.md +58 -0
  16. package/docs/ISSUES_149_150_151.md +77 -0
  17. package/docs/ISSUE_123_FOLLOWUP.md +3 -2
  18. package/docs/ISSUE_146_REVIEW_EVIDENCE.md +53 -0
  19. package/docs/LEARNING.md +1 -1
  20. package/docs/MOBILE_READINESS.md +62 -0
  21. package/docs/PLUGIN_MAINTENANCE.md +1 -3
  22. package/docs/RELEASE_ACCEPTANCE.md +20 -3
  23. package/docs/REVIEW_ROUND_22.md +67 -0
  24. package/docs/REVIEW_ROUND_23.md +48 -0
  25. package/docs/REVIEW_ROUND_25.md +51 -0
  26. package/docs/SELECTION_UPGRADE_REPAIRS.md +2 -2
  27. package/docs/TOOLS.md +181 -604
  28. package/docs/WORKFLOWS.md +5 -5
  29. package/package.json +17 -21
  30. package/packages/openwrite-bridge/lib/client.d.ts +0 -1
  31. package/packages/openwrite-bridge/lib/client.js +90 -9
  32. package/packages/openwrite-bridge/lib/contracts-generated.js +2 -1
  33. package/packages/openwrite-bridge/lib/dog-delivery.d.ts +1 -0
  34. package/packages/openwrite-bridge/lib/dog-delivery.js +3 -1
  35. package/packages/openwrite-bridge/lib/dog-review.js +15 -3
  36. package/packages/openwrite-bridge/lib/domain.d.ts +27 -0
  37. package/packages/openwrite-bridge/lib/domain.js +250 -29
  38. package/packages/openwrite-bridge/lib/host-llm.d.ts +188 -0
  39. package/packages/openwrite-bridge/lib/host-llm.js +657 -0
  40. package/packages/openwrite-bridge/lib/host-web.d.ts +61 -0
  41. package/packages/openwrite-bridge/lib/host-web.js +166 -0
  42. package/packages/openwrite-bridge/lib/index.d.ts +4 -0
  43. package/packages/openwrite-bridge/lib/index.js +150 -6
  44. package/packages/openwrite-bridge/lib/managed-runtime.d.ts +47 -2
  45. package/packages/openwrite-bridge/lib/managed-runtime.js +176 -34
  46. package/packages/openwrite-bridge/lib/tool-policy.js +37 -12
  47. package/packages/openwrite-bridge/lib/tools.js +1223 -1227
  48. package/packages/openwrite-bridge/package.json +7 -5
  49. package/packages/studio-panel/lib/client.js +123207 -121534
  50. package/packages/studio-panel/lib/client.js.map +1 -1
  51. package/packages/studio-panel/lib/types/client/LaunchOpenWrite.d.ts +10 -0
  52. package/packages/studio-panel/lib/types/client/ModelView.d.ts +28 -4
  53. package/packages/studio-panel/lib/types/client/OpenWriteSettings.d.ts +64 -0
  54. package/packages/studio-panel/lib/types/client/SceneWorkbench.d.ts +2 -0
  55. package/packages/studio-panel/lib/types/client/SearchSettings.d.ts +2 -0
  56. package/packages/studio-panel/lib/types/client/SerialWritingView.d.ts +2 -1
  57. package/packages/studio-panel/lib/types/client/TurnMutationSummary.d.ts +2 -0
  58. package/packages/studio-panel/lib/types/client/VditorBody.d.ts +1 -1
  59. package/packages/studio-panel/lib/types/client/api.d.ts +41 -2
  60. package/packages/studio-panel/lib/types/client/browser-download.d.ts +4 -0
  61. package/packages/studio-panel/lib/types/client/companions.d.ts +46 -0
  62. package/packages/studio-panel/lib/types/client/dto.d.ts +22 -0
  63. package/packages/studio-panel/lib/types/client/locales.d.ts +1 -1
  64. package/packages/studio-panel/lib/types/client/workspace-chrome.d.ts +41 -0
  65. package/packages/studio-panel/package.json +5 -3
  66. package/plugin.mjs +159 -20
  67. package/preset-definition.mjs +144 -4
  68. package/presets/openwrite/agent.cordis.yml +7 -9
  69. package/presets/openwrite/ops-integration.mjs +28 -0
  70. package/presets/openwrite/preset.yml +2 -1
  71. package/presets/openwrite/skills/canon/SKILL.md +3 -3
  72. package/presets/openwrite/skills/delivery-artifact-query/SKILL.md +41 -0
  73. package/presets/openwrite/skills/dialoguequality/SKILL.md +16 -17
  74. package/presets/openwrite/skills/dog-delivery-verification/SKILL.md +113 -0
  75. package/presets/openwrite/skills/export-book/SKILL.md +3 -3
  76. package/presets/openwrite/skills/foreshadow/SKILL.md +1 -1
  77. package/presets/openwrite/skills/foreshadowing-system/SKILL.md +16 -25
  78. package/presets/openwrite/skills/import-artifact-query/SKILL.md +30 -0
  79. package/presets/openwrite/skills/learn/SKILL.md +3 -3
  80. package/presets/openwrite/skills/novel-creator/SKILL.md +10 -10
  81. package/presets/openwrite/skills/novel-reviewer/SKILL.md +11 -11
  82. package/presets/openwrite/skills/progress/SKILL.md +1 -1
  83. package/presets/openwrite/skills/review-artifact-query/SKILL.md +41 -0
  84. package/presets/openwrite/skills/review-chapter/SKILL.md +2 -2
  85. package/presets/openwrite/skills/revise-span/SKILL.md +5 -5
  86. package/presets/openwrite/skills/style-system/SKILL.md +13 -6
  87. package/presets/openwrite/skills/truth-validation/SKILL.md +9 -12
  88. package/presets/openwrite/skills/workflow-manager/SKILL.md +13 -13
  89. package/presets/openwrite/skills/world-query/SKILL.md +16 -11
  90. package/presets/openwrite/skills/write-next/SKILL.md +1 -1
  91. package/release/{openwrite-5.8.20-py3-none-any.whl → openwrite-5.8.30-py3-none-any.whl} +0 -0
  92. package/release/runtime-manifest.json +4 -13
  93. package/release/source.json +21 -21
  94. package/release/wheels/licenses/OPENSSL-3.6.3-LICENSE.txt +225 -0
  95. package/scripts/maintenance.mjs +67 -7
  96. package/scripts/plugin-doctor.mjs +179 -0
  97. package/scripts/prepare.mjs +26 -4
  98. package/scripts/safe-upgrade.mjs +38 -5
  99. package/dog-tools.mjs +0 -13
  100. package/presets/openwrite/skills/dog-delivery-query/SKILL.md +0 -42
  101. package/presets/openwrite/skills/dog-import-query/SKILL.md +0 -29
  102. package/presets/openwrite/skills/dog-review-query/SKILL.md +0 -42
  103. package/scripts/dog/delivery-stage.js +0 -32
  104. package/scripts/dog/import-chapter.js +0 -20
  105. package/scripts/dog/import-record.js +0 -35
  106. package/scripts/dog/review-dimension.js +0 -41
  107. package/scripts/dog/review-record.js +0 -42
  108. package/vendor/dsh-dog/LICENSE +0 -29
  109. package/vendor/dsh-dog/README.md +0 -114
  110. package/vendor/dsh-dog/UPSTREAM.md +0 -30
  111. package/vendor/dsh-dog/cordis.patch.yml +0 -14
  112. package/vendor/dsh-dog/lib/client.js +0 -2960
  113. package/vendor/dsh-dog/lib/client.js.map +0 -1
  114. package/vendor/dsh-dog/lib/core--byUPJL8.js +0 -2191
  115. package/vendor/dsh-dog/lib/core-BiEBMLz3.d.ts +0 -495
  116. package/vendor/dsh-dog/lib/core.d.ts +0 -2
  117. package/vendor/dsh-dog/lib/core.js +0 -2
  118. package/vendor/dsh-dog/lib/index.d.ts +0 -143
  119. package/vendor/dsh-dog/lib/index.js +0 -1230
  120. package/vendor/dsh-dog/package.json +0 -110
  121. package/vendor/dsh-dog/schemas/schema-0.2/graph.schema.json +0 -139
  122. package/vendor/dsh-dog/schemas/schema-0.2/report.schema.json +0 -87
  123. package/vendor/dsh-dog/schemas/schema-0.2/run.schema.json +0 -259
  124. package/vendor/dsh-dog/schemas/schema-0.2/runtime-event.schema.json +0 -105
  125. package/vendor/dsh-dog/schemas/schema-0.2/verification.schema.json +0 -40
@@ -8,14 +8,14 @@ Language: follow the author's current conversation language for responses and he
8
8
 
9
9
  # 世界查询系统
10
10
 
11
- 把 `data/novels/{novel_id}/src/` 下的人物与世界实体文件作为唯一关系真源。Studio 拓扑和 ReAct 的 `get_world_relations` 必须调用同一个拓扑查询,不维护 `graph.yaml` 或第二套关系数据库。
11
+ 把 `data/novels/{novel_id}/src/` 下的人物与世界实体文件作为唯一关系真源。Studio 拓扑与 `novel_continuity` 必须调用同一份资产数据,不维护 `graph.yaml` 或第二套关系数据库。
12
12
 
13
13
  ## 查询
14
14
 
15
- - 用 `query_world` 查询实体列表或详情。
16
- - 用 `get_world_relations` 读取与 Studio 连续性页面同源的节点和边。
15
+ - 用 `novel_assets(action="list", kind="world")` / `novel_assets(action="list", kind="character")` 查询实体列表。
16
+ - 用 `novel_assets(action="read", kind="world", id=…)` 读单个实体详情。
17
17
  - Studio 关系拓扑支持按节点名称、ID、类型、摘要和关系文字搜索;命中节点会保留一跳相邻节点和连线作为上下文。
18
- - ReAct 没有第二套拓扑搜索索引;需要搜索时读取 `get_world_relations` 后在同一份节点/边结果中按上述字段过滤。
18
+ - ReAct 没有第二套拓扑搜索索引;需要搜索时先 `novel_assets(action="list")` 再在同一份结果里按上述字段过滤。
19
19
  - 先用 ID 定位;用户只提供名称时允许按 front matter 名称或 Markdown H1 定位。
20
20
 
21
21
  兼容读取以下关系来源:
@@ -28,14 +28,19 @@ Language: follow the author's current conversation language for responses and he
28
28
 
29
29
  ## 增量修改关系
30
30
 
31
- 新建、更新或删除正式关系时使用 `edit_world_relation`,并固定执行两阶段确认:
31
+ 新建、更新或删除正式关系一律走变更计划的两阶段确认,不直接写文件:
32
32
 
33
- 1. 先以 `confirm=false` 调用,保存返回的 `base_revision`,向用户展示 diff。此步不得写文件。
34
- 2. 只有用户明确确认后,才传回同一个 `base_revision` 并设置 `confirm=true`。
35
- 3. 若返回 `relation_revision_conflict`,重新预览;不得复用旧 revision 或整文件重写。
36
- 4. 用户拒绝、犹豫或只是讨论关系时,不得确认写入。
33
+ 1. `novel_change_plan(action="structured", change_kind="asset", step="preview", change=<一条 novel_assets 形状的变更>)`,
34
+ 保存返回的 `preview_token`,向作者展示 diff。此步不写文件。
35
+ 2. 只有作者明确确认后,才 `step="apply"` 并传回同一个 `preview_token`。
36
+ 3. 若返回 `DOCUMENT_CONFLICT` 或预览 token 失效,重新 preview;不得复用旧 token 或整文件重写。
37
+ 4. 作者拒绝、犹豫或只是讨论关系时,不得 apply。
37
38
 
38
- 正式修改统一写入源文件 TOML front matter:
39
+ 作者已在本轮明确授权过这条精确变更时,才可用直接兼容入口
40
+ `novel_assets(action="update", kind="character"|"world", id=…, revision=<刚才 list 得到的 revision>, data={…})`。
41
+ `revision` 必须来自刚刚一次 `novel_assets(action="list")`,否则服务端会以 409 拒绝。
42
+
43
+ 正式关系写在源文件 front matter:
39
44
 
40
45
  ```toml
41
46
  [[related]]
@@ -44,4 +49,4 @@ kind = "related"
44
49
  note = "共同调查旧案"
45
50
  ```
46
51
 
47
- 使用 `action="upsert"` 新增或更新,使用 `action="remove"` 删除。源和目标必须是已有实体,禁止自关系。旧 Markdown 关系段落继续作为兼容只读来源;不要为了修改一条边而覆盖人物卡正文。
52
+ 源和目标必须是已有实体,禁止自关系。旧 Markdown 关系段落继续作为兼容只读来源;不要为了修改一条边而覆盖人物卡正文。
@@ -10,7 +10,7 @@ Language: follow the author's current conversation language for responses and he
10
10
  接到已有 `novel_*`。已有正文不得覆盖。
11
11
 
12
12
  1. `novel_status` 看阶段与待确认项;`outline_scope` 待确认时先让作者确认大纲范围。
13
- 2. `novel_outline_read` 定位章纲:未指定则取大纲顺序中最早尚无正文的章。
13
+ 2. `novel_outline_read` 定位章纲,并读它的 `recommendation`。未指定章节时**省略 `chapter_id`**,让服务端取大纲推荐的下一章——自己推算的 id 只要和推荐不一致就会被拒绝。
14
14
  3. `novel_context_preview` 预检上下文包。
15
15
  4. `novel_chapter_work` 读取本章到期/超期/待埋入伏笔与字数目标。
16
16
  5. `novel_write_chapter` 写下一章或指定章。
@@ -3,7 +3,7 @@
3
3
  "dsh": "0.1.2-rc.1",
4
4
  "uv_version": "0.11.19",
5
5
  "python_version": "3.12.13",
6
- "core_version": "5.8.20",
6
+ "core_version": "5.8.30",
7
7
  "contract_version": 1,
8
8
  "platforms": {
9
9
  "darwin-arm64": {
@@ -56,8 +56,8 @@
56
56
  }
57
57
  },
58
58
  "wheel": {
59
- "file": "openwrite-5.8.20-py3-none-any.whl",
60
- "sha256": "aa1f25f3c615497cd73c3c059fd2acc4e71ba2e936765d02331d1382df4b201a"
59
+ "file": "openwrite-5.8.30-py3-none-any.whl",
60
+ "sha256": "d9e48cbaa0263c7cc194005606a61abdd9194e56adb5e3365b52e4c2be805a37"
61
61
  },
62
62
  "requirements": {
63
63
  "file": "requirements.lock",
@@ -67,16 +67,7 @@
67
67
  "core": {
68
68
  "repository": "https://github.com/LiPu-jpg/Openwrite",
69
69
  "branch": "native-core",
70
- "commit": "9611c654e5d77bcb5968105a7adfa8e9b4f9624a"
71
- },
72
- "dog": {
73
- "repository": "https://github.com/Fun10165/dsh-dog",
74
- "tag": "v1.2.0",
75
- "commit": "3a442ef4b1a82cc4258a01baf798913c94a99445",
76
- "patches": [
77
- "0.2.19: src/host-shims.ts 内置 dshHomePath/defineTool,移除 @deepseek-ai/dsh-home-paths 与 @deepseek-ai/dsh-tools 的运行时导入(#74)",
78
- "unreleased 0.2.28: Electron Node-mode verifier execution, structured process/output diagnostics, and per-condition DoG guards (#121)"
79
- ]
70
+ "commit": "82bae73fd2e1cab88dc05f36cc3945c2d6ee6756"
80
71
  },
81
72
  "cryptography": {
82
73
  "package": "cryptography",
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "repository": "https://github.com/LiPu-jpg/Openwrite",
3
- "commit": "9390c7826024ee78cc91d5a144825a55ca0016e0",
4
- "tree": "d59236a0d5dd77ce0d960586cd7c59eeaf0b96c9",
3
+ "commit": "4538026daf85cb0ce5652d8f0fc831173f5981b9",
4
+ "tree": "ed35517a1e41632b60e07076cf27227184163ee0",
5
5
  "dirty": false,
6
6
  "acquisition": "git-checkout",
7
- "sourceTreeSha256": "b257753216e9ff918bae3ef04487531cb27d186f65d2e0cc613343c4663db1c8",
7
+ "sourceTreeSha256": "2945bc2f3de4f83878d633788d7ef2628fa0929a6b1a64beee51db5b23f79817",
8
8
  "components": [
9
9
  {
10
10
  "name": "@deepseek-ai/schemastery",
@@ -119,6 +119,24 @@
119
119
  "LICENSE"
120
120
  ]
121
121
  },
122
+ {
123
+ "name": "js-yaml",
124
+ "version": "4.3.1",
125
+ "license": "MIT",
126
+ "repository": "nodeca/js-yaml",
127
+ "files": [
128
+ "LICENSE"
129
+ ]
130
+ },
131
+ {
132
+ "name": "argparse",
133
+ "version": "2.0.1",
134
+ "license": "Python-2.0",
135
+ "repository": "nodeca/argparse",
136
+ "files": [
137
+ "LICENSE"
138
+ ]
139
+ },
122
140
  {
123
141
  "name": "proper-lockfile",
124
142
  "version": "4.1.2",
@@ -236,24 +254,6 @@
236
254
  "LICENSE.md"
237
255
  ]
238
256
  },
239
- {
240
- "name": "js-yaml",
241
- "version": "4.3.1",
242
- "license": "MIT",
243
- "repository": "nodeca/js-yaml",
244
- "files": [
245
- "LICENSE"
246
- ]
247
- },
248
- {
249
- "name": "argparse",
250
- "version": "2.0.1",
251
- "license": "Python-2.0",
252
- "repository": "nodeca/argparse",
253
- "files": [
254
- "LICENSE"
255
- ]
256
- },
257
257
  {
258
258
  "name": "@xyflow/react",
259
259
  "version": "12.11.6",
@@ -0,0 +1,225 @@
1
+ OpenSSL 3.6.3 — Apache License 2.0
2
+
3
+ This file accompanies the OpenWrite-built cryptography 50.0.1 wheel
4
+ (cryptography-50.0.1-cp312-abi3-macosx_13_0_x86_64.whl), which statically links
5
+ OpenSSL 3.6.3 as recorded in release/wheels/provenance.json:
6
+
7
+ /openssl = OpenSSL 3.6.3 9 Jun 2026 (Library: OpenSSL 3.6.3 9 Jun 2026)
8
+
9
+ OpenSSL 3.x is licensed under the Apache License, Version 2.0
10
+ (https://www.openssl.org/source/apache-license-2.0.txt). The license text below
11
+ is reproduced verbatim from the Apache License 2.0 shipped inside that wheel at
12
+
13
+ cryptography-50.0.1.dist-info/licenses/LICENSE.APACHE
14
+
15
+ Its SHA-256 is recorded at the end of this file so the copy can be verified
16
+ against the wheel it came from.
17
+
18
+ --------------------------------------------------------------------------
19
+
20
+
21
+ Apache License
22
+ Version 2.0, January 2004
23
+ https://www.apache.org/licenses/
24
+
25
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
26
+
27
+ 1. Definitions.
28
+
29
+ "License" shall mean the terms and conditions for use, reproduction,
30
+ and distribution as defined by Sections 1 through 9 of this document.
31
+
32
+ "Licensor" shall mean the copyright owner or entity authorized by
33
+ the copyright owner that is granting the License.
34
+
35
+ "Legal Entity" shall mean the union of the acting entity and all
36
+ other entities that control, are controlled by, or are under common
37
+ control with that entity. For the purposes of this definition,
38
+ "control" means (i) the power, direct or indirect, to cause the
39
+ direction or management of such entity, whether by contract or
40
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
41
+ outstanding shares, or (iii) beneficial ownership of such entity.
42
+
43
+ "You" (or "Your") shall mean an individual or Legal Entity
44
+ exercising permissions granted by this License.
45
+
46
+ "Source" form shall mean the preferred form for making modifications,
47
+ including but not limited to software source code, documentation
48
+ source, and configuration files.
49
+
50
+ "Object" form shall mean any form resulting from mechanical
51
+ transformation or translation of a Source form, including but
52
+ not limited to compiled object code, generated documentation,
53
+ and conversions to other media types.
54
+
55
+ "Work" shall mean the work of authorship, whether in Source or
56
+ Object form, made available under the License, as indicated by a
57
+ copyright notice that is included in or attached to the work
58
+ (an example is provided in the Appendix below).
59
+
60
+ "Derivative Works" shall mean any work, whether in Source or Object
61
+ form, that is based on (or derived from) the Work and for which the
62
+ editorial revisions, annotations, elaborations, or other modifications
63
+ represent, as a whole, an original work of authorship. For the purposes
64
+ of this License, Derivative Works shall not include works that remain
65
+ separable from, or merely link (or bind by name) to the interfaces of,
66
+ the Work and Derivative Works thereof.
67
+
68
+ "Contribution" shall mean any work of authorship, including
69
+ the original version of the Work and any modifications or additions
70
+ to that Work or Derivative Works thereof, that is intentionally
71
+ submitted to Licensor for inclusion in the Work by the copyright owner
72
+ or by an individual or Legal Entity authorized to submit on behalf of
73
+ the copyright owner. For the purposes of this definition, "submitted"
74
+ means any form of electronic, verbal, or written communication sent
75
+ to the Licensor or its representatives, including but not limited to
76
+ communication on electronic mailing lists, source code control systems,
77
+ and issue tracking systems that are managed by, or on behalf of, the
78
+ Licensor for the purpose of discussing and improving the Work, but
79
+ excluding communication that is conspicuously marked or otherwise
80
+ designated in writing by the copyright owner as "Not a Contribution."
81
+
82
+ "Contributor" shall mean Licensor and any individual or Legal Entity
83
+ on behalf of whom a Contribution has been received by Licensor and
84
+ subsequently incorporated within the Work.
85
+
86
+ 2. Grant of Copyright License. Subject to the terms and conditions of
87
+ this License, each Contributor hereby grants to You a perpetual,
88
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
89
+ copyright license to reproduce, prepare Derivative Works of,
90
+ publicly display, publicly perform, sublicense, and distribute the
91
+ Work and such Derivative Works in Source or Object form.
92
+
93
+ 3. Grant of Patent License. Subject to the terms and conditions of
94
+ this License, each Contributor hereby grants to You a perpetual,
95
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
96
+ (except as stated in this section) patent license to make, have made,
97
+ use, offer to sell, sell, import, and otherwise transfer the Work,
98
+ where such license applies only to those patent claims licensable
99
+ by such Contributor that are necessarily infringed by their
100
+ Contribution(s) alone or by combination of their Contribution(s)
101
+ with the Work to which such Contribution(s) was submitted. If You
102
+ institute patent litigation against any entity (including a
103
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
104
+ or a Contribution incorporated within the Work constitutes direct
105
+ or contributory patent infringement, then any patent licenses
106
+ granted to You under this License for that Work shall terminate
107
+ as of the date such litigation is filed.
108
+
109
+ 4. Redistribution. You may reproduce and distribute copies of the
110
+ Work or Derivative Works thereof in any medium, with or without
111
+ modifications, and in Source or Object form, provided that You
112
+ meet the following conditions:
113
+
114
+ (a) You must give any other recipients of the Work or
115
+ Derivative Works a copy of this License; and
116
+
117
+ (b) You must cause any modified files to carry prominent notices
118
+ stating that You changed the files; and
119
+
120
+ (c) You must retain, in the Source form of any Derivative Works
121
+ that You distribute, all copyright, patent, trademark, and
122
+ attribution notices from the Source form of the Work,
123
+ excluding those notices that do not pertain to any part of
124
+ the Derivative Works; and
125
+
126
+ (d) If the Work includes a "NOTICE" text file as part of its
127
+ distribution, then any Derivative Works that You distribute must
128
+ include a readable copy of the attribution notices contained
129
+ within such NOTICE file, excluding those notices that do not
130
+ pertain to any part of the Derivative Works, in at least one
131
+ of the following places: within a NOTICE text file distributed
132
+ as part of the Derivative Works; within the Source form or
133
+ documentation, if provided along with the Derivative Works; or,
134
+ within a display generated by the Derivative Works, if and
135
+ wherever such third-party notices normally appear. The contents
136
+ of the NOTICE file are for informational purposes only and
137
+ do not modify the License. You may add Your own attribution
138
+ notices within Derivative Works that You distribute, alongside
139
+ or as an addendum to the NOTICE text from the Work, provided
140
+ that such additional attribution notices cannot be construed
141
+ as modifying the License.
142
+
143
+ You may add Your own copyright statement to Your modifications and
144
+ may provide additional or different license terms and conditions
145
+ for use, reproduction, or distribution of Your modifications, or
146
+ for any such Derivative Works as a whole, provided Your use,
147
+ reproduction, and distribution of the Work otherwise complies with
148
+ the conditions stated in this License.
149
+
150
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
151
+ any Contribution intentionally submitted for inclusion in the Work
152
+ by You to the Licensor shall be under the terms and conditions of
153
+ this License, without any additional terms or conditions.
154
+ Notwithstanding the above, nothing herein shall supersede or modify
155
+ the terms of any separate license agreement you may have executed
156
+ with Licensor regarding such Contributions.
157
+
158
+ 6. Trademarks. This License does not grant permission to use the trade
159
+ names, trademarks, service marks, or product names of the Licensor,
160
+ except as required for reasonable and customary use in describing the
161
+ origin of the Work and reproducing the content of the NOTICE file.
162
+
163
+ 7. Disclaimer of Warranty. Unless required by applicable law or
164
+ agreed to in writing, Licensor provides the Work (and each
165
+ Contributor provides its Contributions) on an "AS IS" BASIS,
166
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
167
+ implied, including, without limitation, any warranties or conditions
168
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
169
+ PARTICULAR PURPOSE. You are solely responsible for determining the
170
+ appropriateness of using or redistributing the Work and assume any
171
+ risks associated with Your exercise of permissions under this License.
172
+
173
+ 8. Limitation of Liability. In no event and under no legal theory,
174
+ whether in tort (including negligence), contract, or otherwise,
175
+ unless required by applicable law (such as deliberate and grossly
176
+ negligent acts) or agreed to in writing, shall any Contributor be
177
+ liable to You for damages, including any direct, indirect, special,
178
+ incidental, or consequential damages of any character arising as a
179
+ result of this License or out of the use or inability to use the
180
+ Work (including but not limited to damages for loss of goodwill,
181
+ work stoppage, computer failure or malfunction, or any and all
182
+ other commercial damages or losses), even if such Contributor
183
+ has been advised of the possibility of such damages.
184
+
185
+ 9. Accepting Warranty or Additional Liability. While redistributing
186
+ the Work or Derivative Works thereof, You may choose to offer,
187
+ and charge a fee for, acceptance of support, warranty, indemnity,
188
+ or other liability obligations and/or rights consistent with this
189
+ License. However, in accepting such obligations, You may act only
190
+ on Your own behalf and on Your sole responsibility, not on behalf
191
+ of any other Contributor, and only if You agree to indemnify,
192
+ defend, and hold each Contributor harmless for any liability
193
+ incurred by, or claims asserted against, such Contributor by reason
194
+ of your accepting any such warranty or additional liability.
195
+
196
+ END OF TERMS AND CONDITIONS
197
+
198
+ APPENDIX: How to apply the Apache License to your work.
199
+
200
+ To apply the Apache License to your work, attach the following
201
+ boilerplate notice, with the fields enclosed by brackets "[]"
202
+ replaced with your own identifying information. (Don't include
203
+ the brackets!) The text should be enclosed in the appropriate
204
+ comment syntax for the file format. We also recommend that a
205
+ file or class name and description of purpose be included on the
206
+ same "printed page" as the copyright notice for easier
207
+ identification within third-party archives.
208
+
209
+ Copyright [yyyy] [name of copyright owner]
210
+
211
+ Licensed under the Apache License, Version 2.0 (the "License");
212
+ you may not use this file except in compliance with the License.
213
+ You may obtain a copy of the License at
214
+
215
+ https://www.apache.org/licenses/LICENSE-2.0
216
+
217
+ Unless required by applicable law or agreed to in writing, software
218
+ distributed under the License is distributed on an "AS IS" BASIS,
219
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
220
+ See the License for the specific language governing permissions and
221
+ limitations under the License.
222
+
223
+ SHA-256 of the Apache License 2.0 text above (as shipped in
224
+ cryptography-50.0.1.dist-info/licenses/LICENSE.APACHE):
225
+ aac73b3148f6d1d7111dbca32099f68d26c644c6813ae1e4f05f6579aa2663fe
@@ -13,17 +13,24 @@ export const legacyBundles = legacyPackages
13
13
  const PROFILE = /^[a-zA-Z0-9_-]+$/
14
14
  const PROFILE_FILES = ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', 'cordis.patch.yml']
15
15
 
16
- export function inspectProfile(manifest) {
16
+ export function inspectProfile(manifest, options = {}) {
17
17
  const bundles = Array.isArray(manifest?.dsh?.profile?.bundles) ? manifest.dsh.profile.bundles : []
18
18
  const dependencies = manifest?.dependencies && typeof manifest.dependencies === 'object'
19
19
  ? Object.keys(manifest.dependencies) : []
20
20
  const leftoverBundles = legacyPackages.filter(name => bundles.includes(name))
21
21
  const leftoverDeps = legacyPackages.filter(name => dependencies.includes(name))
22
+ // A manifest that names the bundle while node_modules is gone is not a working
23
+ // install; the caller passes bundlePresent so `installed` can say so. Without
24
+ // it the diagnostic reported "installed: true" and the healthy instruction on
25
+ // a profile that cannot boot.
26
+ const bundlePresent = options.bundlePresent !== false
22
27
  return {
23
28
  leftover: [...new Set([...leftoverBundles, ...leftoverDeps])],
24
29
  leftoverBundles,
25
30
  leftoverDeps,
26
- installed: bundles.filter(name => name === 'dsh-openwrite').length === 1,
31
+ installed: bundlePresent && bundles.filter(name => name === 'dsh-openwrite').length === 1,
32
+ bundlePresent,
33
+ malformedBundles: manifest?.dsh?.profile?.bundles !== undefined && !Array.isArray(manifest.dsh.profile.bundles),
27
34
  otherPlugins: bundles.filter(name => name !== 'dsh-openwrite' && !legacyPackages.includes(name)),
28
35
  }
29
36
  }
@@ -33,8 +40,20 @@ export async function inspectPresets(home) {
33
40
  const names = await readdir(dir).catch(error => { if (error.code === 'ENOENT') return []; throw error })
34
41
  const presets = []
35
42
  for (const name of names) {
36
- const marker = await readFile(join(dir, name, '.openwrite-managed.json'), 'utf8').then(JSON.parse).catch(() => null)
37
- presets.push({ name, origin: marker ? 'package-managed' : 'author-owned', preserved: true })
43
+ const markerPath = join(dir, name, '.openwrite-managed.json')
44
+ const raw = await readFile(markerPath, 'utf8').catch(() => null)
45
+ // A marker that exists but does not parse is not the same as no marker: a
46
+ // crash during the install (or a hand edit) can leave a truncated file, and
47
+ // treating that as "author-owned" made the preset permanently unupgradable
48
+ // with no diagnostic. Report it distinctly so the fix (re-run the installer)
49
+ // is visible.
50
+ let marker = null
51
+ let origin = 'author-owned'
52
+ if (raw !== null) {
53
+ try { marker = JSON.parse(raw) } catch { marker = null; origin = 'marker-corrupt' }
54
+ if (marker) origin = 'package-managed'
55
+ }
56
+ presets.push({ name, origin, preserved: true, marker })
38
57
  }
39
58
  return presets
40
59
  }
@@ -126,10 +145,16 @@ export async function migrate(home, profile, apply = false, options = {}) {
126
145
  leftover: inspection.leftover, leftoverBundles: inspection.leftoverBundles, leftoverDeps: inspection.leftoverDeps,
127
146
  })
128
147
  const runPlugin = options.runPlugin ?? defaultRunPlugin
148
+ // The spec each removed dependency was installed from, so a failure midway can
149
+ // put the packages back instead of leaving a half-migrated profile whose
150
+ // manifest lists dependencies node_modules no longer contains.
151
+ const removedSpecs = []
129
152
  try {
130
153
  if (await readFile(file, 'utf8') !== original) throw new Error('Profile changed during backup; retry with dsh stopped')
131
154
  for (const name of inspection.leftoverDeps) {
155
+ const spec = manifest.dependencies?.[name]
132
156
  await runPlugin(home, profile, ['plugin', '--profile', profile, 'remove', '-w', name])
157
+ if (typeof spec === 'string') removedSpecs.push(spec)
133
158
  }
134
159
  const afterRemove = await readFile(file, 'utf8')
135
160
  const remaining = inspectProfile(JSON.parse(afterRemove))
@@ -141,6 +166,12 @@ export async function migrate(home, profile, apply = false, options = {}) {
141
166
  return { ...base, leftover: [], leftoverBundles: [], leftoverDeps: [], changed: true, backup, otherPlugins: final.otherPlugins }
142
167
  } catch (error) {
143
168
  await restoreBackup(backup, home, profile)
169
+ // Restoring the manifest alone is not enough: the host remove already
170
+ // deleted these packages from node_modules. Put them back so the profile the
171
+ // user is left with is the one the manifest describes.
172
+ for (const spec of removedSpecs) {
173
+ await runPlugin(home, profile, ['plugin', '--profile', profile, 'add', '-w', spec]).catch(() => {})
174
+ }
144
175
  error.message = `${error.message}; restored profile from ${backup}`
145
176
  throw error
146
177
  }
@@ -148,9 +179,27 @@ export async function migrate(home, profile, apply = false, options = {}) {
148
179
 
149
180
  export async function diagnose(home, profile) {
150
181
  const { manifest } = await readManifest(home, profile)
151
- const inspection = inspectProfile(manifest)
152
182
  const presets = await inspectPresets(home)
153
183
  const runtime = await readFile(join(home, 'openwrite', 'active.json'), 'utf8').then(JSON.parse).catch(() => null)
184
+ const installedPackage = await readFile(join(home, 'profiles', profile, 'node_modules', 'dsh-openwrite', 'package.json'), 'utf8').then(JSON.parse).catch(() => null)
185
+ // A manifest that names the bundle while the tree is absent is not a working
186
+ // install. Reading the package first lets `installed` say so, instead of
187
+ // reporting healthy on a profile that cannot boot.
188
+ const inspection = inspectProfile(manifest, { bundlePresent: installedPackage !== null })
189
+ const toolPackage = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'))
190
+ const requestedSpec = manifest.dependencies?.['dsh-openwrite']
191
+ const packageSelection = {
192
+ // A private registry/git URL can contain credentials in its authority,
193
+ // query, fragment or path. Do not copy it into a shareable diagnostic.
194
+ requestedSpec: typeof requestedSpec === 'string'
195
+ ? requestedSpec.includes('://') || /^[^\s@]+@[^\s:]+:/.test(requestedSpec) ? '[remote package URL redacted]' : requestedSpec
196
+ : null,
197
+ resolvedVersion: installedPackage?.version ?? null,
198
+ diagnosticToolVersion: toolPackage.version,
199
+ matchesDiagnosticTool: installedPackage?.version === toolPackage.version,
200
+ reinstallTarget: `dsh-openwrite@${toolPackage.version}`,
201
+ instructions: 'After stopping dsh and backing up the profile, explicitly select a verified version or @latest; a bare add may preserve the existing dependency selection. See docs/INSTALL.md#stale-package-selection.',
202
+ }
154
203
  return {
155
204
  node: process.version, platform: process.platform, arch: process.arch, profile,
156
205
  installed: inspection.installed,
@@ -161,19 +210,30 @@ export async function diagnose(home, profile) {
161
210
  otherPlugins: inspection.otherPlugins,
162
211
  presets,
163
212
  runtimePrepared: runtime !== null,
213
+ packageSelection,
214
+ bundlePresent: inspection.bundlePresent,
215
+ malformedBundles: inspection.malformedBundles,
164
216
  instructions: inspection.leftover.length
165
217
  ? 'Stop dsh, run openwrite-maintenance migrate --profile web --apply, then install the Release package. Author-owned presets and historical sessions are preserved.'
166
- : 'OpenWrite → 诊断与帮助:查看实时健康状态;本命令不启动模型。',
218
+ : !inspection.bundlePresent
219
+ ? 'The manifest lists dsh-openwrite but node_modules/dsh-openwrite is missing, so the plugin cannot load. Stop dsh, then reinstall with dsh plugin add dsh-openwrite@<verified version>.'
220
+ : inspection.malformedBundles
221
+ ? 'dsh.profile.bundles is not an array, so no bundle can be registered. Restore the profile manifest from a backup or reinstall the plugin.'
222
+ : 'OpenWrite → 诊断与帮助:查看实时健康状态;本命令不启动模型。',
167
223
  }
168
224
  }
169
225
 
170
226
  if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
171
227
  const command = process.argv[2] ?? 'doctor'
172
228
  const index = process.argv.indexOf('--profile')
173
- const profile = index < 0 ? 'web' : process.argv[index + 1]
174
229
  const home = resolve(process.env.DSH_HOME || join(homedir(), '.dsh'))
175
230
  try {
176
231
  if (!['doctor', 'migrate'].includes(command)) throw new Error('Usage: openwrite-maintenance doctor|migrate --profile web [--apply]')
232
+ // `--profile` with no value must be a usage error. `PROFILE.test(undefined)`
233
+ // coerces to the string "undefined", which the pattern accepts, so the guard
234
+ // downstream cannot catch it and the failure surfaces as a raw TypeError.
235
+ if (index >= 0 && process.argv[index + 1] === undefined) throw new Error('Usage: openwrite-maintenance doctor|migrate --profile <name> [--apply]')
236
+ const profile = index < 0 ? 'web' : process.argv[index + 1]
177
237
  console.log(JSON.stringify(await (command === 'migrate' ? migrate(home, profile, process.argv.includes('--apply')) : diagnose(home, profile)), null, 2))
178
238
  } catch (error) { console.error(error.message); process.exitCode = 1 }
179
239
  }